phetsims / tandem

Simulation-side code for PhET-iO
MIT License
0 stars 5 forks source link

addLinkedElement should only PhetioObjects #299

Closed zepumph closed 1 year ago

zepumph commented 1 year ago

We support LinkableElement for our common UI code, but we should be type safe in calling addLinkedElement, and only do that for PhetioObjects. @samreid and I were working on a patch and made it work well in initial testing, we will take a look.

zepumph commented 1 year ago

There are 23 spots (as well as all future spots) where we would have to handle this in sim-specific usages instead of generally. I would say this is not correct or a step in the right direction.

@samreid please continue if you would like, but most likely this should have a conversation with CM before committing. Feel free to close.

```diff Subject: [PATCH] support not just if I am featured, but if any element descendant is featured, thus reflecting if I am in the "featured display tree", https://github.com/phetsims/studio/issues/305 --- Index: tandem/js/PhetioObject.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/tandem/js/PhetioObject.ts b/tandem/js/PhetioObject.ts --- a/tandem/js/PhetioObject.ts (revision b6b351e668dbe368ce2c82e7d1729f0bc2cdec36) +++ b/tandem/js/PhetioObject.ts (date 1686602803377) @@ -582,7 +582,7 @@ * @param element - the target element. Must be instrumented for a LinkedElement to be created-- otherwise gracefully opts out * @param [providedOptions] */ - public addLinkedElement( element: LinkableElement, providedOptions?: LinkedElementOptions ): void { + public addLinkedElement( element: PhetioObject, providedOptions?: LinkedElementOptions ): void { if ( !this.isPhetioInstrumented() ) { // set this to null so that you can't addLinkedElement on an uninitialized PhetioObject and then instrument @@ -851,9 +851,9 @@ * Internal class to avoid cyclic dependencies. */ class LinkedElement extends PhetioObject { - public readonly element: LinkableElement; + public readonly element: PhetioObject; - public constructor( coreElement: LinkableElement, providedOptions?: LinkedElementOptions ) { + public constructor( coreElement: PhetioObject, providedOptions?: LinkedElementOptions ) { assert && assert( !!coreElement, 'coreElement should be defined' ); const options = optionize()( { Index: sun/js/ComboBox.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/sun/js/ComboBox.ts b/sun/js/ComboBox.ts --- a/sun/js/ComboBox.ts (revision 03f17578e663f394d5fc943550b5b2c06d0bd3b7) +++ b/sun/js/ComboBox.ts (date 1686602887161) @@ -42,6 +42,7 @@ import GroupItemOptions, { getGroupItemNodes } from './GroupItemOptions.js'; import Multilink from '../../axon/js/Multilink.js'; import StrictOmit from '../../phet-core/js/types/StrictOmit.js'; +import PhetioObject from '../../tandem/js/PhetioObject.js'; // const const LIST_POSITION_VALUES = [ 'above', 'below' ] as const; // where the list pops up relative to the button @@ -417,7 +418,7 @@ this.pickable = !displayOnly; } ); - this.addLinkedElement( property, { + property instanceof PhetioObject && this.addLinkedElement( property, { tandem: options.tandem.createTandem( 'property' ) } ); Index: sun/js/Slider.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/sun/js/Slider.ts b/sun/js/Slider.ts --- a/sun/js/Slider.ts (revision 03f17578e663f394d5fc943550b5b2c06d0bd3b7) +++ b/sun/js/Slider.ts (date 1686602860736) @@ -42,6 +42,7 @@ import LinkableElement from '../../tandem/js/LinkableElement.js'; import PickRequired from '../../phet-core/js/types/PickRequired.js'; import isSettingPhetioStateProperty from '../../tandem/js/isSettingPhetioStateProperty.js'; +import PhetioObject from '../../tandem/js/PhetioObject.js'; // constants const DEFAULT_HORIZONTAL_TRACK_SIZE = new Dimension2( 100, 5 ); @@ -499,7 +500,7 @@ // Must happen after instrumentation (in super call) const linkedProperty = options.phetioLinkedProperty || ( valueProperty instanceof ReadOnlyProperty ? valueProperty : null ); - if ( linkedProperty ) { + if ( linkedProperty && linkedProperty instanceof PhetioObject ) { this.addLinkedElement( linkedProperty, { tandem: options.tandem.createTandem( 'valueProperty' ) } ); Index: sun/js/Checkbox.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/sun/js/Checkbox.ts b/sun/js/Checkbox.ts --- a/sun/js/Checkbox.ts (revision 03f17578e663f394d5fc943550b5b2c06d0bd3b7) +++ b/sun/js/Checkbox.ts (date 1686602832862) @@ -225,7 +225,7 @@ this.setExcludeLabelSiblingFromInput(); // must be after the Checkbox is instrumented - options.phetioLinkProperty && this.addLinkedElement( property, { + options.phetioLinkProperty && property instanceof PhetioObject && this.addLinkedElement( property, { tandem: options.tandem.createTandem( 'property' ) } ); Index: scenery-phet/js/ZoomButtonGroup.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/scenery-phet/js/ZoomButtonGroup.ts b/scenery-phet/js/ZoomButtonGroup.ts --- a/scenery-phet/js/ZoomButtonGroup.ts (revision 7c1f820ca11b669541b65e044e58f19e70d74c67) +++ b/scenery-phet/js/ZoomButtonGroup.ts (date 1686602913854) @@ -14,6 +14,7 @@ import sceneryPhet from './sceneryPhet.js'; import TRangedProperty from '../../axon/js/TRangedProperty.js'; import SceneryPhetStrings from './SceneryPhetStrings.js'; +import PhetioObject from '../../tandem/js/PhetioObject.js'; type SelfOptions = { @@ -129,7 +130,7 @@ }; zoomLevelProperty.link( zoomLevelListener ); - this.addLinkedElement( zoomLevelProperty, { + zoomLevelProperty instanceof PhetioObject && this.addLinkedElement( zoomLevelProperty, { tandem: options.tandem.createTandem( 'zoomProperty' ) } );
samreid commented 1 year ago

I ~feel~ suspect this is the appropriate way to go, and the price of type checks at each site is worth it. But I also agree we should discuss it with @pixelzoom before proceeding.

@pixelzoom the main proposal is to change PhetioObject.addLinkedElement from:

public addLinkedElement( element: LinkableElement, providedOptions?: LinkedElementOptions ): void {

to

public addLinkedElement( element: PhetioObject, providedOptions?: LinkedElementOptions ): void {

recall LinkableElement is defined as type LinkableElement = Pick<PhetioObject, 'phetioFeatured' | 'isPhetioInstrumented'>;

This means that call sites would need to guard like so:

    if ( linkedProperty && linkedProperty instanceof PhetioObject ) {
      this.addLinkedElement( linkedProperty, {
        tandem: options.tandem.createTandem( 'valueProperty' )
      } );
    }

There are more examples of this in @zepumph patch. @pixelzoom can you please review this issue and advise?

pixelzoom commented 1 year ago

The API change to addLinkedElement sounds reasonable. And that would be consistent with removeLinkedElement, which currently requires a PhetioObject:

 public removeLinkedElements( potentiallyLinkedElement: PhetioObject )

But is it going to be a problem not to have a link back to the core element? That one of the things that LinkedElement provides:

class LinkedElement extends PhetioObject {
  public readonly element: LinkableElement;

Here's a patch to PhetioObject that makes addLinkedElement require a PhetioObject:

patch ```diff Subject: [PATCH] more doc, https://github.com/phetsims/phet-io/issues/1943 --- Index: js/PhetioObject.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/js/PhetioObject.ts b/js/PhetioObject.ts --- a/js/PhetioObject.ts (revision b4da95b7d1b3b7a01fe165221dba9a8c7b4d6554) +++ b/js/PhetioObject.ts (date 1686631723570) @@ -166,7 +166,7 @@ // Public only for PhetioObjectMetadataInput public phetioArchetypePhetioID!: string | null; - private linkedElements!: LinkedElement[] | null; + private linkedElements!: PhetioObject[] | null; public phetioNotifiedObjectCreated!: boolean; private phetioMessageStack!: number[]; public static readonly DEFAULT_OPTIONS = DEFAULTS; @@ -582,7 +582,7 @@ * @param element - the target element. Must be instrumented for a LinkedElement to be created-- otherwise gracefully opts out * @param [providedOptions] */ - public addLinkedElement( element: LinkableElement, providedOptions?: LinkedElementOptions ): void { + public addLinkedElement( element: PhetioObject, providedOptions?: LinkedElementOptions ): void { if ( !this.isPhetioInstrumented() ) { // set this to null so that you can't addLinkedElement on an uninitialized PhetioObject and then instrument ```

Results of tsc on the entire code base:

Found 24 errors in 16 files.

Errors Files 2 ../../../beers-law-lab/js/beerslaw/model/BeersLawSolution.ts:120 1 ../../../beers-law-lab/js/common/model/Solute.ts:118 1 ../../../calculus-grapher/js/common/view/LabeledLinesNode.ts:53 1 ../../../calculus-grapher/js/common/view/LabeledPointsNode.ts:45 1 ../../../equality-explorer/js/common/model/Variable.ts:66 1 ../../../equality-explorer/js/solveit/model/SolveItLevel.ts:138 1 ../../../joist/js/Screen.ts:193 1 ../../../ph-scale/js/common/model/Solute.ts:105 6 ../../../ph-scale/js/common/view/graph/GraphNode.ts:176 3 ../../../ph-scale/js/common/view/ParticleCountsNode.ts:125 1 ../../../ph-scale/js/micro/view/MicroPHAccordionBox.ts:51 1 ../../../scenery-phet/js/ZoomButtonGroup.ts:132 1 ../../../sun/js/Checkbox.ts:228 1 ../../../sun/js/ComboBox.ts:420 1 ../../../sun/js/Slider.ts:503 1 ../../../tandem/js/PhetioObject.ts:616

samreid commented 1 year ago

Yes, with this proposal we would also change:

class LinkedElement extends PhetioObject {
  public readonly element: LinkableElement;

to

class LinkedElement extends PhetioObject {
  public readonly element: PhetioObject;

However the patch in the preceding comment incorrectly changes private linkedElements!: LinkedElement[] | null; to private linkedElements!: PhetioObject[] | null;. Perhaps due to a confusion between LinkedElement vs LinkableElement?

pixelzoom commented 1 year ago

Ah, sorry -- indeed, my patch changed a few internal things incorrectly. But the patch was intended mainly to determine how many errors would occur by changing the signature of addLinkedElement. And (after correcting my patch) external to PhetioObject, I still see 23 errors in the files listed below. I looked through the first few files, and they would take some time to address. You might want to inspect them too before proceeding.

2 ../../../beers-law-lab/js/beerslaw/model/BeersLawSolution.ts:120 1 ../../../beers-law-lab/js/common/model/Solute.ts:118 1 ../../../calculus-grapher/js/common/view/LabeledLinesNode.ts:53 1 ../../../calculus-grapher/js/common/view/LabeledPointsNode.ts:45 1 ../../../equality-explorer/js/common/model/Variable.ts:66 1 ../../../equality-explorer/js/solveit/model/SolveItLevel.ts:138 1 ../../../joist/js/Screen.ts:193 1 ../../../ph-scale/js/common/model/Solute.ts:105 6 ../../../ph-scale/js/common/view/graph/GraphNode.ts:176 3 ../../../ph-scale/js/common/view/ParticleCountsNode.ts:125 1 ../../../ph-scale/js/micro/view/MicroPHAccordionBox.ts:51 1 ../../../scenery-phet/js/ZoomButtonGroup.ts:132 1 ../../../sun/js/Checkbox.ts:228 1 ../../../sun/js/ComboBox.ts:420 1 ../../../sun/js/Slider.ts:503

zepumph commented 1 year ago

@pixelzoom, I am not worried about the 23 usages. As I reported in https://github.com/phetsims/tandem/issues/299#issuecomment-1588075815, instead I am interested in how we feel about this pattern generally. Would it be acceptable to use an instanceof check before calls to addLinkedElement in your code when the types don't align directly into the ReadOnlyProperty hierarchy (or elsewhere).

I'm would be a bit surprised if you preferred this direction. The reason why we are here is because of a single instanceof assertion/type cast in phet-io internal code, and the result is a propagation of 23+ instanceof checks in common code. I would expect we would prefer a graceful API where addLinkedElement can be added to any interface as a no-op to support a simpler common code library. I can't say I care too much either way though.

pixelzoom commented 1 year ago

Would it be acceptable to use an instanceof check before calls to addLinkedElement in your code when the types don't align directly into the ReadOnlyProperty hierarchy (or elsewhere).

My first reaction is "that sounds awful". My second reaction is "show me an example before I give you my first reaction" :)

zepumph commented 1 year ago

The patch in https://github.com/phetsims/tandem/issues/299#issuecomment-1588075815 has multiple examples, sorry if that wasn't clear.

pixelzoom commented 1 year ago

Example for ComboBox:

-    this.addLinkedElement( property, {
+    property instanceof PhetioObject && this.addLinkedElement( property, {
       tandem: options.tandem.createTandem( 'property' )
     } );

No, I don't like that direction.

samreid commented 1 year ago

@zepumph how about changing the signature of addLinkedElement like this:

```diff Subject: [PATCH] test --- Index: js/PhetioObject.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/js/PhetioObject.ts b/js/PhetioObject.ts --- a/js/PhetioObject.ts (revision 0be325692d5571532089c9616bf9a758c31a8340) +++ b/js/PhetioObject.ts (date 1686683235032) @@ -28,7 +28,6 @@ import IOType from './types/IOType.js'; import IntentionalAny from '../../phet-core/js/types/IntentionalAny.js'; import Disposable from '../../axon/js/Disposable.js'; -import LinkableElement from './LinkableElement.js'; // constants const PHET_IO_ENABLED = Tandem.PHET_IO_ENABLED; @@ -582,7 +581,7 @@ * @param element - the target element. Must be instrumented for a LinkedElement to be created-- otherwise gracefully opts out * @param [providedOptions] */ - public addLinkedElement( element: LinkableElement, providedOptions?: LinkedElementOptions ): void { + public addLinkedElement( element: PhetioObject | unknown, providedOptions?: LinkedElementOptions ): void { if ( !this.isPhetioInstrumented() ) { // set this to null so that you can't addLinkedElement on an uninitialized PhetioObject and then instrument @@ -593,7 +592,7 @@ // In some cases, UI components need to be wired up to a private (internal) Property which should neither be // instrumented nor linked. - if ( PHET_IO_ENABLED && element.isPhetioInstrumented() ) { + if ( PHET_IO_ENABLED && element instanceof PhetioObject && element.isPhetioInstrumented() ) { const options = optionize()( { // The linkage is only featured if the parent and the element are both also featured @@ -733,7 +732,7 @@ // TODO: linkedChild.element is not a full PhetioObject, see https://github.com/phetsims/tandem/issues/299 // This may be solved by making addLinkedElement parameter type PhetioObject assert && assert( linkedChild.element instanceof PhetioObject, 'linkedChild.element should be a PhetioObject, ' + this.phetioID ); - return linkedChild.element as PhetioObject; + return linkedChild.element; } /** @@ -851,9 +850,9 @@ * Internal class to avoid cyclic dependencies. */ class LinkedElement extends PhetioObject { - public readonly element: LinkableElement; + public readonly element: PhetioObject; - public constructor( coreElement: LinkableElement, providedOptions?: LinkedElementOptions ) { + public constructor( coreElement: PhetioObject, providedOptions?: LinkedElementOptions ) { assert && assert( !!coreElement, 'coreElement should be defined' ); const options = optionize()( { ```

If we go with that, how would we change usages like in Slider.ts?

zepumph commented 1 year ago

@samreid and I really like the above patch. It moves the burden onto PhetioObject.addLinkedElement(), and allows us to completely get rid of the Linkable* types that we created in https://github.com/phetsims/axon/issues/382. In my opinion those are clunky and not actually solving any problems. We would like to talk @pixelzoom before continuing though. I'll reach out as priority permits.

```diff Subject: [PATCH] run PhET-iO reset code independent of provided listener, https://github.com/phetsims/sun/issues/598 --- Index: center-and-variability/js/variability/view/VariabilityMeasureCheckbox.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/center-and-variability/js/variability/view/VariabilityMeasureCheckbox.ts b/center-and-variability/js/variability/view/VariabilityMeasureCheckbox.ts --- a/center-and-variability/js/variability/view/VariabilityMeasureCheckbox.ts (revision fdd66311e80a517c8be88163b5f1dfbebaeaa5aa) +++ b/center-and-variability/js/variability/view/VariabilityMeasureCheckbox.ts (date 1686688581968) @@ -5,12 +5,12 @@ import { AlignGroup, Color, Node, Rectangle, TColor, Text } from '../../../../scenery/js/imports.js'; import CAVConstants from '../../common/CAVConstants.js'; import AccordionBoxCheckboxFactory from '../../common/view/AccordionBoxCheckboxFactory.js'; -import LinkableProperty from '../../../../axon/js/LinkableProperty.js'; import TReadOnlyProperty from '../../../../axon/js/TReadOnlyProperty.js'; import IconArrowNode from './IconArrowNode.js'; +import TProperty from '../../../../axon/js/TProperty.js'; export default class VariabilityMeasureCheckbox extends Checkbox { - public constructor( property: LinkableProperty, stringProperty: TReadOnlyProperty, iconGroup: AlignGroup, textGroup: AlignGroup, color: TColor, options: CheckboxOptions ) { + public constructor( property: TProperty, stringProperty: TReadOnlyProperty, iconGroup: AlignGroup, textGroup: AlignGroup, color: TColor, options: CheckboxOptions ) { const rectangle = new Rectangle( 0, 0, 25, 25, { fill: color, Index: beers-law-lab/js/beerslaw/model/BeersLawSolution.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/beers-law-lab/js/beerslaw/model/BeersLawSolution.ts b/beers-law-lab/js/beerslaw/model/BeersLawSolution.ts --- a/beers-law-lab/js/beerslaw/model/BeersLawSolution.ts (revision ba35eb35c66269c9ed712c54afa23573b9da8b9d) +++ b/beers-law-lab/js/beerslaw/model/BeersLawSolution.ts (date 1686688270586) @@ -30,7 +30,7 @@ import MolarAbsorptivityData from './MolarAbsorptivityData.js'; import TReadOnlyProperty from '../../../../axon/js/TReadOnlyProperty.js'; import optionize from '../../../../phet-core/js/optionize.js'; -import LinkableProperty from '../../../../axon/js/LinkableProperty.js'; +import TProperty from '../../../../axon/js/TProperty.js'; // parent tandem for all static instances of BeersLawSolution const SOLUTIONS_TANDEM = Tandem.ROOT.createTandem( 'beersLawScreen' ).createTandem( 'model' ).createTandem( 'solutions' ); @@ -38,8 +38,8 @@ type SelfOptions = { // required - nameProperty: LinkableProperty; // name that is visible to the user - formulaProperty: LinkableProperty; // formula that is visible to the user, null defaults to nameProperty.value + nameProperty: TProperty; // name that is visible to the user + formulaProperty: TProperty; // formula that is visible to the user, null defaults to nameProperty.value molarAbsorptivityData: MolarAbsorptivityData; concentrationRange: RangeWithValue; concentrationTransform: ConcentrationTransform; Index: calculus-grapher/js/common/view/LabeledLinesNode.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/calculus-grapher/js/common/view/LabeledLinesNode.ts b/calculus-grapher/js/common/view/LabeledLinesNode.ts --- a/calculus-grapher/js/common/view/LabeledLinesNode.ts (revision 08db488e9603c74044e30ddf61e3aee9c0c80df6) +++ b/calculus-grapher/js/common/view/LabeledLinesNode.ts (date 1686688581996) @@ -13,10 +13,10 @@ import LabeledLine from '../model/LabeledLine.js'; import LabeledLineNode, { LabeledLineNodeOptions } from './LabeledLineNode.js'; import ChartTransform from '../../../../bamboo/js/ChartTransform.js'; -import LinkableElement from '../../../../tandem/js/LinkableElement.js'; import PickRequired from '../../../../phet-core/js/types/PickRequired.js'; import optionize from '../../../../phet-core/js/optionize.js'; import StrictOmit from '../../../../phet-core/js/types/StrictOmit.js'; +import PhetioObject from '../../../../tandem/js/PhetioObject.js'; type SelfOptions = { labeledLineOptions?: LabeledLineNodeOptions; // propagated to LabeledLineNode @@ -28,7 +28,7 @@ public readonly labeledLineNodes: LabeledLineNode[]; - public constructor( labeledLines: LabeledLine[], labeledLinesLinkableElement: LinkableElement, + public constructor( labeledLines: LabeledLine[], labeledLinesLinkableElement: PhetioObject, chartTransform: ChartTransform, providedOptions: LabeledLinesNodeOptions ) { const options = optionize, NodeOptions>()( { Index: beers-law-lab/js/common/model/Solute.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/beers-law-lab/js/common/model/Solute.ts b/beers-law-lab/js/common/model/Solute.ts --- a/beers-law-lab/js/common/model/Solute.ts (revision ba35eb35c66269c9ed712c54afa23573b9da8b9d) +++ b/beers-law-lab/js/common/model/Solute.ts (date 1686688581955) @@ -6,7 +6,6 @@ * @author Chris Malley (PixelZoom, Inc.) */ -import LinkableProperty from '../../../../axon/js/LinkableProperty.js'; import Property from '../../../../axon/js/Property.js'; import optionize from '../../../../phet-core/js/optionize.js'; import PickRequired from '../../../../phet-core/js/types/PickRequired.js'; @@ -21,6 +20,7 @@ import BeersLawLabStrings from '../../BeersLawLabStrings.js'; import SoluteColorScheme from '../../concentration/model/SoluteColorScheme.js'; import Solvent from './Solvent.js'; +import TProperty from '../../../../axon/js/TProperty.js'; // parent tandem for all static instances of Solute const SOLUTES_TANDEM = Tandem.GLOBAL_MODEL.createTandem( 'solutes' ); @@ -28,7 +28,7 @@ type SelfOptions = { // required - nameProperty: LinkableProperty; + nameProperty: TProperty; stockSolutionConcentration: number; // mol/L molarMass: number; // g/mol colorScheme: SoluteColorScheme; @@ -49,10 +49,10 @@ // the solute's tandem name, used to create other tandems that pertain to this solute public readonly tandemName: string; - public readonly nameProperty: LinkableProperty; + public readonly nameProperty: TProperty; // Added for PhET-iO, see https://github.com/phetsims/beers-law-lab/issues/272 - public readonly formulaProperty: LinkableProperty; + public readonly formulaProperty: TProperty; public readonly stockSolutionConcentration: number; // mol/L public readonly molarMass: number; // g/mol Index: equality-explorer/js/solveit/model/SolveItLevel.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/equality-explorer/js/solveit/model/SolveItLevel.ts b/equality-explorer/js/solveit/model/SolveItLevel.ts --- a/equality-explorer/js/solveit/model/SolveItLevel.ts (revision c0d9c4f1c388616c99872b7e2248b59a17470a92) +++ b/equality-explorer/js/solveit/model/SolveItLevel.ts (date 1686688670282) @@ -7,7 +7,6 @@ * @author Chris Malley (PixelZoom, Inc.) */ -import LinkableProperty from '../../../../axon/js/LinkableProperty.js'; import NumberProperty, { DEFAULT_RANGE } from '../../../../axon/js/NumberProperty.js'; import Property from '../../../../axon/js/Property.js'; import dotRandom from '../../../../dot/js/dotRandom.js'; @@ -27,6 +26,7 @@ import Challenge from './Challenge.js'; import ChallengeGenerator from './ChallengeGenerator.js'; import DebugChallenge from './DebugChallenge.js'; +import TProperty from '../../../../axon/js/TProperty.js'; // constants const POINTS_PER_CHALLENGE = 1; @@ -43,7 +43,7 @@ public readonly levelNumber: number; // description that appears in the UI for the level - public readonly descriptionProperty: LinkableProperty; + public readonly descriptionProperty: TProperty; public readonly challengeGenerator: ChallengeGenerator; public readonly scoreProperty: NumberProperty; @@ -62,7 +62,7 @@ private readonly rightVariableTermCreator: VariableTermCreator; private readonly rightConstantTermCreator: ConstantTermCreator; - public constructor( descriptionProperty: LinkableProperty, + public constructor( descriptionProperty: TProperty, challengeGenerator: ChallengeGenerator, providedOptions: SolveItLevelOptions ) { Index: joist/js/Screen.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/joist/js/Screen.ts b/joist/js/Screen.ts --- a/joist/js/Screen.ts (revision 316ac964d60e79cdbe0f8c9b7cb620379772904b) +++ b/joist/js/Screen.ts (date 1686688670275) @@ -34,7 +34,7 @@ import Multilink from '../../axon/js/Multilink.js'; import TModel from './TModel.js'; import PatternStringProperty from '../../axon/js/PatternStringProperty.js'; -import LinkableProperty from '../../axon/js/LinkableProperty.js'; +import TProperty from '../../axon/js/TProperty.js'; const screenNamePatternStringProperty = JoistStrings.a11y.screenNamePatternStringProperty; const screenSimPatternStringProperty = JoistStrings.a11y.screenSimPatternStringProperty; @@ -53,7 +53,7 @@ // Documentation is by the defaults type SelfOptions = { - name?: LinkableProperty | null; + name?: TProperty | null; instrumentNameProperty?: boolean; // It would be preferable to support Property solely, but many subtypes are hardcoded to be Color only Index: keplers-laws/js/common/view/KeplersLawsOrbitalInformationBox.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/keplers-laws/js/common/view/KeplersLawsOrbitalInformationBox.ts b/keplers-laws/js/common/view/KeplersLawsOrbitalInformationBox.ts --- a/keplers-laws/js/common/view/KeplersLawsOrbitalInformationBox.ts (revision e9b66b66360d9980867ba0a205aebc48b71ed676) +++ b/keplers-laws/js/common/view/KeplersLawsOrbitalInformationBox.ts (date 1686688670288) @@ -14,12 +14,12 @@ import SolarSystemCommonColors from '../../../../solar-system-common/js/SolarSystemCommonColors.js'; import SolarSystemCommonConstants from '../../../../solar-system-common/js/SolarSystemCommonConstants.js'; import KeplersLawsModel from '../model/KeplersLawsModel.js'; -import LinkableProperty from '../../../../axon/js/LinkableProperty.js'; import TReadOnlyProperty from '../../../../axon/js/TReadOnlyProperty.js'; import DerivedProperty from '../../../../axon/js/DerivedProperty.js'; import keplersLaws from '../../keplersLaws.js'; import FirstLawCheckboxIcons from '../../first-law/FirstLawCheckboxIcons.js'; import KeplersLawsStrings from '../../KeplersLawsStrings.js'; +import TProperty from '../../../../axon/js/TProperty.js'; type SelfOptions = EmptySelfOptions; @@ -35,7 +35,7 @@ }; const createCheckbox = ( - property: LinkableProperty, + property: TProperty, text: TReadOnlyProperty, tandemName: string, icon: Node = new Node(), Index: ph-scale/js/common/model/Solute.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/ph-scale/js/common/model/Solute.ts b/ph-scale/js/common/model/Solute.ts --- a/ph-scale/js/common/model/Solute.ts (revision 1e218bb75409935ab858cb74e9c18f6d10ffcc11) +++ b/ph-scale/js/common/model/Solute.ts (date 1686688447540) @@ -21,6 +21,7 @@ import PhScaleStrings from '../../PhScaleStrings.js'; import PHScaleConstants from '../PHScaleConstants.js'; import Water from './Water.js'; +import TProperty from '../../../../axon/js/TProperty.js'; // parent tandem for static instances of Solute, which are used across all screens const SOLUTES_TANDEM = Tandem.GLOBAL_MODEL.createTandem( 'solutes' ); @@ -69,7 +70,7 @@ * @param stockColor - color of the solute in stock solution (no dilution) * @param [provideOptions] */ - public constructor( nameProperty: LinkableProperty, pH: number, stockColor: Color, provideOptions: SoluteOptions ) { + public constructor( nameProperty: TProperty, pH: number, stockColor: Color, provideOptions: SoluteOptions ) { assert && assert( PHScaleConstants.PH_RANGE.contains( pH ), `invalid pH: ${pH}` ); Index: ph-scale/js/common/model/Solution.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/ph-scale/js/common/model/Solution.ts b/ph-scale/js/common/model/Solution.ts --- a/ph-scale/js/common/model/Solution.ts (revision 1e218bb75409935ab858cb74e9c18f6d10ffcc11) +++ b/ph-scale/js/common/model/Solution.ts (date 1686688228376) @@ -43,7 +43,7 @@ public readonly soluteVolumeProperty: Property; public readonly waterVolumeProperty: Property; public readonly totalVolumeProperty: TReadOnlyProperty; - public readonly pHProperty: LinkableReadOnlyProperty; + public readonly pHProperty: TReadOnlyProperty; public readonly colorProperty: TReadOnlyProperty; public readonly maxVolume: number; Index: chipper/js/grunt/modulify.js IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/chipper/js/grunt/modulify.js b/chipper/js/grunt/modulify.js --- a/chipper/js/grunt/modulify.js (revision 6946e2a60323a50550d9a4860fad93794f0432d1) +++ b/chipper/js/grunt/modulify.js (date 1686688228438) @@ -396,7 +396,7 @@ // Add ; to the last in the list text = replace( text, ': string\n', ': string;\n' ); - text = replace( text, ': LinkableProperty\n', ': LinkableProperty;\n' ); + text = replace( text, ': TProperty\n', ': TProperty;\n' ); // Use ; instead of , text = replace( text, ',', ';' ); Index: ph-scale/js/common/model/SolutionDerivedProperties.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/ph-scale/js/common/model/SolutionDerivedProperties.ts b/ph-scale/js/common/model/SolutionDerivedProperties.ts --- a/ph-scale/js/common/model/SolutionDerivedProperties.ts (revision 1e218bb75409935ab858cb74e9c18f6d10ffcc11) +++ b/ph-scale/js/common/model/SolutionDerivedProperties.ts (date 1686688447575) @@ -22,10 +22,8 @@ import { PhetioObjectOptions } from '../../../../tandem/js/PhetioObject.js'; import NullableIO from '../../../../tandem/js/types/NullableIO.js'; import NumberIO from '../../../../tandem/js/types/NumberIO.js'; -import PHModel from './PHModel.js'; +import PHModel, { ConcentrationValue, PHValue } from './PHModel.js'; import phScale from '../../phScale.js'; -import { ConcentrationValue, PHValue } from './PHModel.js'; -import LinkableReadOnlyProperty from '../../../../axon/js/LinkableReadOnlyProperty.js'; type SelfOptions = EmptySelfOptions; @@ -34,19 +32,19 @@ export default class SolutionDerivedProperties { // The concentration (mol/L) of H2O, H3O+, and OH- in the solution - public readonly concentrationH2OProperty: LinkableReadOnlyProperty; - public readonly concentrationH3OProperty: LinkableReadOnlyProperty; - public readonly concentrationOHProperty: LinkableReadOnlyProperty; + public readonly concentrationH2OProperty: TReadOnlyProperty; + public readonly concentrationH3OProperty: TReadOnlyProperty; + public readonly concentrationOHProperty: TReadOnlyProperty; // The quantity (mol) of H2O, H3O+, and OH- in the solution - public readonly quantityH2OProperty: LinkableReadOnlyProperty; - public readonly quantityH3OProperty: LinkableReadOnlyProperty; - public readonly quantityOHProperty: LinkableReadOnlyProperty; + public readonly quantityH2OProperty: TReadOnlyProperty; + public readonly quantityH3OProperty: TReadOnlyProperty; + public readonly quantityOHProperty: TReadOnlyProperty; // The number of H2O, H3O+, and OH- particles in the solution - public readonly particleCountH2OProperty: LinkableReadOnlyProperty; - public readonly particleCountH3OProperty: LinkableReadOnlyProperty; - public readonly particleCountOHProperty: LinkableReadOnlyProperty; + public readonly particleCountH2OProperty: TReadOnlyProperty; + public readonly particleCountH3OProperty: TReadOnlyProperty; + public readonly particleCountOHProperty: TReadOnlyProperty; public constructor( pHProperty: TReadOnlyProperty, totalVolumeProperty: TReadOnlyProperty, Index: ph-scale/js/micro/view/MicroPHAccordionBox.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/ph-scale/js/micro/view/MicroPHAccordionBox.ts b/ph-scale/js/micro/view/MicroPHAccordionBox.ts --- a/ph-scale/js/micro/view/MicroPHAccordionBox.ts (revision 1e218bb75409935ab858cb74e9c18f6d10ffcc11) +++ b/ph-scale/js/micro/view/MicroPHAccordionBox.ts (date 1686688447526) @@ -16,6 +16,7 @@ import NumberDisplay from '../../../../scenery-phet/js/NumberDisplay.js'; import PhetFont from '../../../../scenery-phet/js/PhetFont.js'; import LinkableReadOnlyProperty from '../../../../axon/js/LinkableReadOnlyProperty.js'; +import TReadOnlyProperty from '../../../../axon/js/TReadOnlyProperty.js'; type SelfOptions = EmptySelfOptions; @@ -28,7 +29,7 @@ * @param probeYOffset - distance from top of meter to tip of probe, in view coordinate frame * @param [providedOptions] */ - public constructor( pHProperty: LinkableReadOnlyProperty, + public constructor( pHProperty: TReadOnlyProperty, probeYOffset: number, providedOptions: MicroPHAccordionBoxOptions ) { Index: tandem/js/PhetioObject.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/tandem/js/PhetioObject.ts b/tandem/js/PhetioObject.ts --- a/tandem/js/PhetioObject.ts (revision 83b24603dc30302338c3521b3e9e2be452fe28a1) +++ b/tandem/js/PhetioObject.ts (date 1686688122707) @@ -28,7 +28,6 @@ import IOType from './types/IOType.js'; import IntentionalAny from '../../phet-core/js/types/IntentionalAny.js'; import Disposable from '../../axon/js/Disposable.js'; -import LinkableElement from './LinkableElement.js'; // constants const PHET_IO_ENABLED = Tandem.PHET_IO_ENABLED; @@ -582,7 +581,7 @@ * @param element - the target element. Must be instrumented for a LinkedElement to be created-- otherwise gracefully opts out * @param [providedOptions] */ - public addLinkedElement( element: LinkableElement, providedOptions?: LinkedElementOptions ): void { + public addLinkedElement( element: PhetioObject | unknown, providedOptions?: LinkedElementOptions ): void { if ( !this.isPhetioInstrumented() ) { // set this to null so that you can't addLinkedElement on an uninitialized PhetioObject and then instrument @@ -593,7 +592,7 @@ // In some cases, UI components need to be wired up to a private (internal) Property which should neither be // instrumented nor linked. - if ( PHET_IO_ENABLED && element.isPhetioInstrumented() ) { + if ( PHET_IO_ENABLED && element instanceof PhetioObject && element.isPhetioInstrumented() ) { const options = optionize()( { // The linkage is only featured if the parent and the element are both also featured @@ -730,10 +729,7 @@ assert && assert( linkedChild, 'phetioElement is needed' ); - // TODO: linkedChild.element is not a full PhetioObject, see https://github.com/phetsims/tandem/issues/299 - // This may be solved by making addLinkedElement parameter type PhetioObject - assert && assert( linkedChild.element instanceof PhetioObject, 'linkedChild.element should be a PhetioObject, ' + this.phetioID ); - return linkedChild.element as PhetioObject; + return linkedChild.element; } /** @@ -851,9 +847,9 @@ * Internal class to avoid cyclic dependencies. */ class LinkedElement extends PhetioObject { - public readonly element: LinkableElement; + public readonly element: PhetioObject; - public constructor( coreElement: LinkableElement, providedOptions?: LinkedElementOptions ) { + public constructor( coreElement: PhetioObject, providedOptions?: LinkedElementOptions ) { assert && assert( !!coreElement, 'coreElement should be defined' ); const options = optionize()( { Index: number-play/js/game/model/NumberPlayGameType.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/number-play/js/game/model/NumberPlayGameType.ts b/number-play/js/game/model/NumberPlayGameType.ts --- a/number-play/js/game/model/NumberPlayGameType.ts (revision 0ce8b8eefb910d8f46a75bc58d131408b0be2efc) +++ b/number-play/js/game/model/NumberPlayGameType.ts (date 1686688670268) @@ -17,7 +17,7 @@ * @author Chris Klusendorf (PhET Interactive Simulations) */ -type LevelDescriptions = Record>; +type LevelDescriptions = Record>; type LevelImages = Record; class NumberPlayGameType extends EnumerationValue { Index: center-and-variability/js/common/view/MaxKicksControlNode.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/center-and-variability/js/common/view/MaxKicksControlNode.ts b/center-and-variability/js/common/view/MaxKicksControlNode.ts --- a/center-and-variability/js/common/view/MaxKicksControlNode.ts (revision fdd66311e80a517c8be88163b5f1dfbebaeaa5aa) +++ b/center-and-variability/js/common/view/MaxKicksControlNode.ts (date 1686688581990) @@ -2,16 +2,16 @@ import { ComboBoxOptions } from '../../../../sun/js/ComboBox.js'; import centerAndVariability from '../../centerAndVariability.js'; -import LinkableProperty from '../../../../axon/js/LinkableProperty.js'; import { HBox, Node, Text } from '../../../../scenery/js/imports.js'; import MaxKicksComboBox from './MaxKicksComboBox.js'; import PickRequired from '../../../../phet-core/js/types/PickRequired.js'; import CAVConstants from '../CAVConstants.js'; import CenterAndVariabilityStrings from '../../CenterAndVariabilityStrings.js'; +import TProperty from '../../../../axon/js/TProperty.js'; export default class MaxKicksControlNode extends HBox { - public constructor( maxKicksProperty: LinkableProperty, listParent: Node, options: ComboBoxOptions & PickRequired ) { + public constructor( maxKicksProperty: TProperty, listParent: Node, options: ComboBoxOptions & PickRequired ) { super( { spacing: 10, children: [ new Text( CenterAndVariabilityStrings.maxKicksForScreensWithPlotsStringProperty, { Index: calculus-grapher/js/common/view/LabeledPointsNode.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/calculus-grapher/js/common/view/LabeledPointsNode.ts b/calculus-grapher/js/common/view/LabeledPointsNode.ts --- a/calculus-grapher/js/common/view/LabeledPointsNode.ts (revision 08db488e9603c74044e30ddf61e3aee9c0c80df6) +++ b/calculus-grapher/js/common/view/LabeledPointsNode.ts (date 1686688581974) @@ -15,12 +15,12 @@ import Tandem from '../../../../tandem/js/Tandem.js'; import ChartTransform from '../../../../bamboo/js/ChartTransform.js'; import TReadOnlyProperty from '../../../../axon/js/TReadOnlyProperty.js'; -import LinkableElement from '../../../../tandem/js/LinkableElement.js'; +import PhetioObject from '../../../../tandem/js/PhetioObject.js'; export default class LabeledPointsNode extends Node { public constructor( labeledPoints: LabeledPoint[], - linkableElement: LinkableElement, + linkableElement: PhetioObject, chartTransform: ChartTransform, predictEnabledProperty: TReadOnlyProperty, curveLayerVisibleProperty: TReadOnlyProperty, Index: sun/js/VSlider.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/sun/js/VSlider.ts b/sun/js/VSlider.ts --- a/sun/js/VSlider.ts (revision 03f17578e663f394d5fc943550b5b2c06d0bd3b7) +++ b/sun/js/VSlider.ts (date 1686688447559) @@ -14,7 +14,7 @@ import sun from './sun.js'; import optionize, { EmptySelfOptions } from '../../phet-core/js/optionize.js'; import StrictOmit from '../../phet-core/js/types/StrictOmit.js'; -import LinkableProperty from '../../axon/js/LinkableProperty.js'; +import TProperty from '../../axon/js/TProperty.js'; type SelfOptions = EmptySelfOptions; @@ -22,7 +22,7 @@ export default class VSlider extends Slider { - public constructor( valueProperty: LinkableProperty, range: Range, options?: VSliderOptions ) { + public constructor( valueProperty: TProperty, range: Range, options?: VSliderOptions ) { options = optionize()( { orientation: Orientation.VERTICAL Index: sun/js/Slider.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/sun/js/Slider.ts b/sun/js/Slider.ts --- a/sun/js/Slider.ts (revision 03f17578e663f394d5fc943550b5b2c06d0bd3b7) +++ b/sun/js/Slider.ts (date 1686688447533) @@ -33,13 +33,11 @@ import SliderTick, { SliderTickOptions } from './SliderTick.js'; import sun from './sun.js'; import PickOptional from '../../phet-core/js/types/PickOptional.js'; -import LinkableProperty from '../../axon/js/LinkableProperty.js'; import Multilink from '../../axon/js/Multilink.js'; import TProperty from '../../axon/js/TProperty.js'; import TinyProperty from '../../axon/js/TinyProperty.js'; import SunConstants from './SunConstants.js'; import createObservableArray, { ObservableArray } from '../../axon/js/createObservableArray.js'; -import LinkableElement from '../../tandem/js/LinkableElement.js'; import PickRequired from '../../phet-core/js/types/PickRequired.js'; import isSettingPhetioStateProperty from '../../tandem/js/isSettingPhetioStateProperty.js'; @@ -99,7 +97,7 @@ // of using the passed in Property. This option was created to support passing DynamicProperty or "wrapping" // Property that are "implementation details" to the PhET-iO API, and still support having a LinkedElement that // points to the underlying model Property. - phetioLinkedProperty?: LinkableElement | null; + phetioLinkedProperty?: TReadOnlyProperty | null; // This is used to generate sounds as the slider is moved by the user. If not provided, the default sound generator // will be created. If set to null, the slider will generate no sound. @@ -149,7 +147,7 @@ // This value is set during thumb drag, or null if not currently being dragged. private proposedValue: number | null = null; - public constructor( valueProperty: LinkableProperty, + public constructor( valueProperty: TProperty, range: Range | TReadOnlyProperty, providedOptions?: SliderOptions ) { Index: sun/js/HSlider.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/sun/js/HSlider.ts b/sun/js/HSlider.ts --- a/sun/js/HSlider.ts (revision 03f17578e663f394d5fc943550b5b2c06d0bd3b7) +++ b/sun/js/HSlider.ts (date 1686688447581) @@ -14,7 +14,7 @@ import sun from './sun.js'; import optionize, { EmptySelfOptions } from '../../phet-core/js/optionize.js'; import StrictOmit from '../../phet-core/js/types/StrictOmit.js'; -import LinkableProperty from '../../axon/js/LinkableProperty.js'; +import TProperty from '../../axon/js/TProperty.js'; type SelfOptions = EmptySelfOptions; @@ -22,7 +22,7 @@ export default class HSlider extends Slider { - public constructor( valueProperty: LinkableProperty, range: Range, options?: HSliderOptions ) { + public constructor( valueProperty: TProperty, range: Range, options?: HSliderOptions ) { super( valueProperty, range, optionize()( { orientation: Orientation.HORIZONTAL Index: sun/js/ComboBox.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/sun/js/ComboBox.ts b/sun/js/ComboBox.ts --- a/sun/js/ComboBox.ts (revision 03f17578e663f394d5fc943550b5b2c06d0bd3b7) +++ b/sun/js/ComboBox.ts (date 1686688447589) @@ -37,11 +37,11 @@ import DerivedProperty from '../../axon/js/DerivedProperty.js'; import IntentionalAny from '../../phet-core/js/types/IntentionalAny.js'; import TReadOnlyProperty from '../../axon/js/TReadOnlyProperty.js'; -import LinkableProperty from '../../axon/js/LinkableProperty.js'; import { SpeakableResolvedResponse } from '../../utterance-queue/js/ResponsePacket.js'; import GroupItemOptions, { getGroupItemNodes } from './GroupItemOptions.js'; import Multilink from '../../axon/js/Multilink.js'; import StrictOmit from '../../phet-core/js/types/StrictOmit.js'; +import TProperty from '../../axon/js/TProperty.js'; // const const LIST_POSITION_VALUES = [ 'above', 'below' ] as const; // where the list pops up relative to the button @@ -177,7 +177,7 @@ * @param listParent node that will be used as the list's parent, use this to ensure that the list is in front of everything else * @param [providedOptions?] */ - public constructor( property: LinkableProperty, items: ComboBoxItem[], listParent: Node, providedOptions?: ComboBoxOptions ) { + public constructor( property: TProperty, items: ComboBoxItem[], listParent: Node, providedOptions?: ComboBoxOptions ) { assert && assert( _.uniqBy( items, ( item: ComboBoxItem ) => item.value ).length === items.length, 'items must have unique values' ); Index: sun/js/Checkbox.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/sun/js/Checkbox.ts b/sun/js/Checkbox.ts --- a/sun/js/Checkbox.ts (revision 03f17578e663f394d5fc943550b5b2c06d0bd3b7) +++ b/sun/js/Checkbox.ts (date 1686688447511) @@ -25,7 +25,7 @@ import Bounds2 from '../../dot/js/Bounds2.js'; import { Shape } from '../../kite/js/imports.js'; import StrictOmit from '../../phet-core/js/types/StrictOmit.js'; -import LinkableProperty from '../../axon/js/LinkableProperty.js'; +import TProperty from '../../axon/js/TProperty.js'; // constants const BOOLEAN_VALIDATOR = { valueType: 'boolean' }; @@ -85,7 +85,7 @@ // Handles layout of the content, rectangles and mouse/touch areas private readonly constraint: CheckboxConstraint; - public constructor( property: LinkableProperty, content: Node, providedOptions?: CheckboxOptions ) { + public constructor( property: TProperty, content: Node, providedOptions?: CheckboxOptions ) { const options = optionize()( { Index: scenery-phet/js/keyboard/TextKeyNode.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/scenery-phet/js/keyboard/TextKeyNode.ts b/scenery-phet/js/keyboard/TextKeyNode.ts --- a/scenery-phet/js/keyboard/TextKeyNode.ts (revision 0872f0aa667f84484a22c43ef8f8ce39775069b7) +++ b/scenery-phet/js/keyboard/TextKeyNode.ts (date 1686688447608) @@ -6,7 +6,6 @@ * @author Jesse Greenberg */ -import LinkableProperty from '../../../axon/js/LinkableProperty.js'; import TReadOnlyProperty from '../../../axon/js/TReadOnlyProperty.js'; import optionize from '../../../phet-core/js/optionize.js'; import platform from '../../../phet-core/js/platform.js'; @@ -15,6 +14,7 @@ import sceneryPhet from '../sceneryPhet.js'; import SceneryPhetStrings from '../SceneryPhetStrings.js'; import KeyNode, { KeyNodeOptions } from './KeyNode.js'; +import TProperty from '../../../axon/js/TProperty.js'; type SelfOptions = { font?: Font; @@ -54,7 +54,7 @@ /** * Returns the correct platform dependent key string for "Alt". "Alt" on Windows, "Option" on Mac. */ - public static getAltKeyString(): LinkableProperty { + public static getAltKeyString(): TProperty { return platform.mac ? SceneryPhetStrings.key.optionStringProperty : SceneryPhetStrings.key.altStringProperty; Index: center-and-variability/js/common/view/PlayAreaCheckboxFactory.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/center-and-variability/js/common/view/PlayAreaCheckboxFactory.ts b/center-and-variability/js/common/view/PlayAreaCheckboxFactory.ts --- a/center-and-variability/js/common/view/PlayAreaCheckboxFactory.ts (revision fdd66311e80a517c8be88163b5f1dfbebaeaa5aa) +++ b/center-and-variability/js/common/view/PlayAreaCheckboxFactory.ts (date 1686688618168) @@ -17,7 +17,6 @@ import CenterAndVariabilityStrings from '../../CenterAndVariabilityStrings.js'; import CAVColors from '../CAVColors.js'; import PredictionThumbNode from './PredictionThumbNode.js'; -import LinkableProperty from '../../../../axon/js/LinkableProperty.js'; import VariabilityModel from '../../variability/model/VariabilityModel.js'; import CAVModel from '../model/CAVModel.js'; import MeanAndMedianModel from '../../mean-and-median/model/MeanAndMedianModel.js'; @@ -28,6 +27,7 @@ import MeanIndicatorNode from './MeanIndicatorNode.js'; import SoccerCommonColors from '../../soccer-common/SoccerCommonColors.js'; import SoccerCommonConstants from '../../soccer-common/SoccerCommonConstants.js'; +import TProperty from '../../../../axon/js/TProperty.js'; // constants const TEXT_OPTIONS = { @@ -113,7 +113,7 @@ }; } - private static createPredictionItem( property: Property, stringProperty: LinkableProperty, color: TColor, spacing: number, + private static createPredictionItem( property: Property, stringProperty: TProperty, color: TColor, spacing: number, tandemName: string, alignGroup: AlignGroup ): VerticalCheckboxGroupItem { return { createNode: ( tandem: Tandem ) => { Index: sound/js/common/SoundScreen.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/sound/js/common/SoundScreen.ts b/sound/js/common/SoundScreen.ts --- a/sound/js/common/SoundScreen.ts (revision cc65a87ae375ad620d15e4271a33f58b6362e6da) +++ b/sound/js/common/SoundScreen.ts (date 1686688447555) @@ -15,10 +15,10 @@ import SoundModel from './model/SoundModel.js'; import SoundScreenView from './view/SoundScreenView.js'; import Tandem from '../../../tandem/js/Tandem.js'; -import LinkableProperty from '../../../axon/js/LinkableProperty.js'; +import TProperty from '../../../axon/js/TProperty.js'; export default class SoundScreen extends Screen { - public constructor( title: LinkableProperty, createModel: () => T, createView: ( model: T ) => SoundScreenView, iconImage: Node ) { + public constructor( title: TProperty, createModel: () => T, createView: ( model: T ) => SoundScreenView, iconImage: Node ) { const options: ScreenOptions = { backgroundColorProperty: SoundColors.SCREEN_VIEW_BACKGROUND, Index: scenery-phet/js/NumberControl.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/scenery-phet/js/NumberControl.ts b/scenery-phet/js/NumberControl.ts --- a/scenery-phet/js/NumberControl.ts (revision 0872f0aa667f84484a22c43ef8f8ce39775069b7) +++ b/scenery-phet/js/NumberControl.ts (date 1686688447549) @@ -11,7 +11,6 @@ */ import DerivedProperty from '../../axon/js/DerivedProperty.js'; -import LinkableProperty from '../../axon/js/LinkableProperty.js'; import Property from '../../axon/js/Property.js'; import TReadOnlyProperty from '../../axon/js/TReadOnlyProperty.js'; import Dimension2 from '../../dot/js/Dimension2.js'; @@ -34,6 +33,7 @@ import NumberDisplay, { NumberDisplayOptions } from './NumberDisplay.js'; import PhetFont from './PhetFont.js'; import sceneryPhet from './sceneryPhet.js'; +import TProperty from '../../axon/js/TProperty.js'; // constants const SPECIFIC_COMPONENT_CALLBACK_OPTIONS = [ @@ -191,7 +191,7 @@ private readonly numberDisplay: NumberDisplay; private readonly disposeNumberControl: () => void; - public constructor( title: string | TReadOnlyProperty, numberProperty: LinkableProperty, numberRange: Range, providedOptions?: NumberControlOptions ) { + public constructor( title: string | TReadOnlyProperty, numberProperty: TProperty, numberRange: Range, providedOptions?: NumberControlOptions ) { // Make sure that general callbacks (for all components) and specific callbacks (for a specific component) aren't // used in tandem. This must be called before defaults are set. Index: center-and-variability/js/mean-and-median/view/MeanAndMedianAccordionBox.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/center-and-variability/js/mean-and-median/view/MeanAndMedianAccordionBox.ts b/center-and-variability/js/mean-and-median/view/MeanAndMedianAccordionBox.ts --- a/center-and-variability/js/mean-and-median/view/MeanAndMedianAccordionBox.ts (revision fdd66311e80a517c8be88163b5f1dfbebaeaa5aa) +++ b/center-and-variability/js/mean-and-median/view/MeanAndMedianAccordionBox.ts (date 1686688581962) @@ -13,13 +13,13 @@ import VerticalCheckboxGroup from '../../../../sun/js/VerticalCheckboxGroup.js'; import CAVConstants from '../../common/CAVConstants.js'; import TReadOnlyProperty from '../../../../axon/js/TReadOnlyProperty.js'; -import LinkableProperty from '../../../../axon/js/LinkableProperty.js'; import Utils from '../../../../dot/js/Utils.js'; import CAVColors from '../../common/CAVColors.js'; import PatternStringProperty from '../../../../axon/js/PatternStringProperty.js'; import DerivedProperty from '../../../../axon/js/DerivedProperty.js'; import AccordionBoxTitleText from '../../common/view/AccordionBoxTitleText.js'; import NumberLineNode from '../../soccer-common/view/NumberLineNode.js'; +import TProperty from '../../../../axon/js/TProperty.js'; export default class MeanAndMedianAccordionBox extends CAVAccordionBox { private readonly medianPlotNode: MeanAndMedianPlotNode; @@ -55,7 +55,7 @@ backgroundNode.addChild( meanAndMedianPlotNode ); const createReadoutText = ( valueProperty: TReadOnlyProperty, visibleProperty: TReadOnlyProperty, - templateStringProperty: LinkableProperty, fill: TPaint, readoutTandem: Tandem ) => { + templateStringProperty: TProperty, fill: TPaint, readoutTandem: Tandem ) => { const readoutProperty = new DerivedProperty( [ valueProperty, CenterAndVariabilityStrings.valueUnknownStringProperty ], ( value, valueUnknownString ) => { Index: equality-explorer/js/common/model/Variable.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/equality-explorer/js/common/model/Variable.ts b/equality-explorer/js/common/model/Variable.ts --- a/equality-explorer/js/common/model/Variable.ts (revision c0d9c4f1c388616c99872b7e2248b59a17470a92) +++ b/equality-explorer/js/common/model/Variable.ts (date 1686688581980) @@ -13,8 +13,8 @@ import TReadOnlyProperty from '../../../../axon/js/TReadOnlyProperty.js'; import PhetioObject, { PhetioObjectOptions } from '../../../../tandem/js/PhetioObject.js'; import PickRequired from '../../../../phet-core/js/types/PickRequired.js'; -import LinkableProperty from '../../../../axon/js/LinkableProperty.js'; import EqualityExplorerConstants from '../EqualityExplorerConstants.js'; +import TProperty from '../../../../axon/js/TProperty.js'; type SelfOptions = { value?: number; // initial value @@ -33,7 +33,7 @@ * @param symbolProperty - the variable's symbol, e.g. 'x' * @param [providedOptions] */ - public constructor( symbolProperty: LinkableProperty, providedOptions: VariableOptions ) { + public constructor( symbolProperty: TProperty, providedOptions: VariableOptions ) { const options = optionize()( { Index: center-and-variability/js/common/view/MaxKicksComboBox.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/center-and-variability/js/common/view/MaxKicksComboBox.ts b/center-and-variability/js/common/view/MaxKicksComboBox.ts --- a/center-and-variability/js/common/view/MaxKicksComboBox.ts (revision fdd66311e80a517c8be88163b5f1dfbebaeaa5aa) +++ b/center-and-variability/js/common/view/MaxKicksComboBox.ts (date 1686688581985) @@ -2,13 +2,13 @@ import ComboBox, { ComboBoxOptions } from '../../../../sun/js/ComboBox.js'; import centerAndVariability from '../../centerAndVariability.js'; -import LinkableProperty from '../../../../axon/js/LinkableProperty.js'; import { Node, Text } from '../../../../scenery/js/imports.js'; import CAVConstants from '../CAVConstants.js'; +import TProperty from '../../../../axon/js/TProperty.js'; export default class MaxKicksComboBox extends ComboBox { - public constructor( maxKicksProperty: LinkableProperty, listParent: Node, providedOptions?: ComboBoxOptions ) { + public constructor( maxKicksProperty: TProperty, listParent: Node, providedOptions?: ComboBoxOptions ) { super( maxKicksProperty, CAVConstants.MAX_KICKS_VALUES.map( value => { return { value: value, Index: axon/js/TinyEmitter.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/axon/js/TinyEmitter.ts b/axon/js/TinyEmitter.ts --- a/axon/js/TinyEmitter.ts (revision 3abee4d1e08a59ff4ab58eccea98bdde6c15f69b) +++ b/axon/js/TinyEmitter.ts (date 1686688195378) @@ -241,6 +241,8 @@ public forEachListener( callback: ( listener: TEmitterListener ) => void ): void { this.listeners.forEach( callback ); } + + public isPhetioInstrumented(): boolean { return false;} } axon.register( 'TinyEmitter', TinyEmitter ); Index: equality-explorer/js/common/view/ObjectPicker.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/equality-explorer/js/common/view/ObjectPicker.ts b/equality-explorer/js/common/view/ObjectPicker.ts --- a/equality-explorer/js/common/view/ObjectPicker.ts (revision c0d9c4f1c388616c99872b7e2248b59a17470a92) +++ b/equality-explorer/js/common/view/ObjectPicker.ts (date 1686688670296) @@ -13,7 +13,6 @@ */ import DerivedProperty from '../../../../axon/js/DerivedProperty.js'; -import LinkableProperty from '../../../../axon/js/LinkableProperty.js'; import Multilink from '../../../../axon/js/Multilink.js'; import NumberProperty from '../../../../axon/js/NumberProperty.js'; import Property from '../../../../axon/js/Property.js'; @@ -25,6 +24,7 @@ import PickRequired from '../../../../phet-core/js/types/PickRequired.js'; import { Color, FireListener, FireListenerOptions, LinearGradient, Node, NodeOptions, Path, PathOptions, Rectangle, TColor } from '../../../../scenery/js/imports.js'; import equalityExplorer from '../../equalityExplorer.js'; +import TProperty from '../../../../axon/js/TProperty.js'; const ButtonStateValues = [ 'up', 'down', 'over', 'out' ] as const; type ButtonState = ( typeof ButtonStateValues )[number]; @@ -78,8 +78,8 @@ // whether increment and decrement are enabled. // If the client provides these, then the client is fully responsible for the state of these Properties. // If null, a default implementation is used. - incrementEnabledProperty?: LinkableProperty | null; - decrementEnabledProperty?: LinkableProperty | null; + incrementEnabledProperty?: TProperty | null; + decrementEnabledProperty?: TProperty | null; }; type ObjectPickerOptions = SelfOptions & PickRequired; Index: axon/js/TReadOnlyProperty.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/axon/js/TReadOnlyProperty.ts b/axon/js/TReadOnlyProperty.ts --- a/axon/js/TReadOnlyProperty.ts (revision 3abee4d1e08a59ff4ab58eccea98bdde6c15f69b) +++ b/axon/js/TReadOnlyProperty.ts (date 1686688195381) @@ -28,6 +28,7 @@ hasListener( listener: PropertyLinkListener ): boolean; isSettable(): boolean; dispose(): void; + isPhetioInstrumented(): boolean; isDisposed?: boolean; toString(): string;
zepumph commented 1 year ago

I put something on the books for monday.

zepumph commented 1 year ago

@samreid @pixelzoom and I all discussed this, and we were really going back and forth about where the burden should be about phet-io support in common code. We landed on a smaller change, since LinkableProperty is central the UI common code, and ensures type safe dependence on PhetioObject. We solidified that a bit more, making it clear that you need to pass a ReadOnlyProperty (PhetioObject) to support linked elements. A typescript-safe approach is best here to help out phet-io developers, and likely there are few cases where open source/non-phetsims usages of sun and scenery-phet would yield problems (like when using TinyProperty).

zepumph commented 1 year ago

Commit coming soon.

zepumph commented 1 year ago

Bringing the summary in https://github.com/phetsims/tandem/issues/299#issuecomment-1599200308 to the bottom of the issue:

@samreid @pixelzoom and I all discussed this, and we were really going back and forth about where the burden should be about phet-io support in common code. We landed on a smaller change, since LinkableProperty is central the UI common code, and ensures type safe dependence on PhetioObject. We solidified that a bit more, making it clear that you need to pass a ReadOnlyProperty (PhetioObject) to support linked elements. A typescript-safe approach is best here to help out phet-io developers, and likely there are few cases where open source/non-phetsims usages of sun and scenery-phet would yield problems (like when using TinyProperty).

Now there are only 19 files using PhetioProperty. This is a big improvement from LinkableProperty, and is only needed where non Property instances needed a settable property interface AND phet-io support at the same time (like dynamic properties). We also kept it in our UI components for that same flexibility.

@samreid should we do any more work to try to limit the current usages of PhetioProperty? I also updated the doc a bit more. Can you please spot check.

samreid commented 1 year ago

I reviewed usages of PhetioProperty and was able to remove one. The rest seem appropriate. I don't feel the name is perfect, but I can't think of a better name. I feel this issue is ready to close.