phetsims / tandem

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

StateSchema `_private` does not have TypeScript support. #282

Closed pixelzoom closed 1 year ago

pixelzoom commented 1 year ago

Related to https://github.com/phetsims/natural-selection/issues/326 ...

When creating an IOType, specifying a stateSchema with a _private field will save the state for those fields, but exclude them from display in Studio. This feature works in JavaScript, but apparently does not have proper TypeScript support. There are currently no TypeScript sims using _private, and natural-selection is the first to do so.

For example, in natural-selection Wolf.ts:

public static readonly WolfIO = new IOType<Wolf, WolfStateObject>( 'WolfIO', {
    valueType: Wolf,
    stateSchema: {

      // private fields, will not be shown in Studio
      _private: {
157     speed: NumberIO
      }
    },
    toStateObject: wolf => wolf.toStateObject(),
    applyState: ( wolf, stateObject ) => wolf.applyState( stateObject ),
    stateToArgsForConstructor: stateObject => Wolf.stateToArgsForConstructor( stateObject )
  } );

Line 157 is flagged with:

TS2322: Type '{ speed: IOType<number, number>; }' is not assignable to type 'IOType<any, any>'.   Object literal may only specify known properties, and 'speed' does not exist in type 'IOType<any, any>'.

@samreid and I looked at IOType.ts and StateSchema.ts, but could not figure out how to support _private.

@zepumph could you please take a look?

samreid commented 1 year ago

@zepumph and I also discussed adding this type:

Record<string,IOType | Record<string|IOType>>

Not perfect, but would help get us past some ts-ignores.

zepumph commented 1 year ago

Also wanted to note this patch which we could pursue later, but I like the above to start.

```diff Index: tandem/js/types/StateSchema.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/tandem/js/types/StateSchema.ts b/tandem/js/types/StateSchema.ts --- a/tandem/js/types/StateSchema.ts (revision 95f5b6286a7d146fab673ed1695d6a74ddd2824a) +++ b/tandem/js/types/StateSchema.ts (date 1669136657465) @@ -20,16 +20,15 @@ import IOType from './IOType.js'; import IntentionalAny from '../../../phet-core/js/types/IntentionalAny.js'; -export type CompositeSchema = Record & { - _private?: CompositeSchema; -}; -type CompositeSchemaAPI = Record & { - _private?: Record; -}; +type NotPrivate = Omit, '_private'>; // eslint-disable-line -export type CompositeStateObjectType = Record & { - _private?: Record; -}; +type Privatize = NotPrivate | ( { + _private?: Record; +} & NotPrivate ); + +export type CompositeSchema = Privatize; +type CompositeSchemaAPI = Privatize; +export type CompositeStateObjectType = Privatize; type StateSchemaOptions = { displayString?: string; Index: natural-selection/js/common/model/Wolf.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/natural-selection/js/common/model/Wolf.ts b/natural-selection/js/common/model/Wolf.ts --- a/natural-selection/js/common/model/Wolf.ts (revision 4bcd6dc299ad375cd79f12082521a861d7a4921c) +++ b/natural-selection/js/common/model/Wolf.ts (date 1669135170361) @@ -153,7 +153,6 @@ // private fields, will not be shown in Studio _private: { - // @ts-ignore https://github.com/phetsims/tandem/issues/282 TypeScript support for _private speed: NumberIO } }, Index: tandem/js/types/IOType.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/tandem/js/types/IOType.ts b/tandem/js/types/IOType.ts --- a/tandem/js/types/IOType.ts (revision 95f5b6286a7d146fab673ed1695d6a74ddd2824a) +++ b/tandem/js/types/IOType.ts (date 1669134146182) @@ -14,7 +14,7 @@ import PhetioConstants from '../PhetioConstants.js'; import TandemConstants, { PhetioObjectMetadata } from '../TandemConstants.js'; import tandemNamespace from '../tandemNamespace.js'; -import StateSchema, { CompositeStateObjectType } from './StateSchema.js'; +import StateSchema, { CompositeSchema, CompositeStateObjectType } from './StateSchema.js'; import PhetioObject from '../PhetioObject.js'; import IntentionalAny from '../../../phet-core/js/types/IntentionalAny.js'; import PhetioDynamicElementContainer from '../PhetioDynamicElementContainer.js'; @@ -64,7 +64,7 @@ type StateSchemaOption = ( ( ioType: IOType ) => StateSchema ) | StateSchema | - Record | + CompositeSchema | null; type StateOptions = {
pixelzoom commented 1 year ago

I should also point out that the _private convention in StateSchema does not seem to be well-known, and therefore not consistently followed. For example, in greenhouse Wave.ts, WaveIO does not use _private to serialize the private fields of Wave (propagationLimit, renderingWavelength, etc.). @jbphet

jbphet commented 1 year ago

@samreid and @zepumph - I was mentioned above in this issue, so now I'm wondering whether I should modify the code in greenhouse-effect to use _private. What do you recommend?

samreid commented 1 year ago

Does Greenhouse PhET-iO state data have values that are more of an "implementation detail" than a "user-facing feature"? If so, those values should be nested under _private.

jbphet commented 1 year ago

I don't want to hijack this issue with questions specific to Greenhouse Effect, so I've set up an issue where I'll try to get information on how to use _private appropriately in that sim.

pixelzoom commented 1 year ago

This issue is identified as one of the many problems with BunnyIO in https://github.com/phetsims/natural-selection/issues/327.

pixelzoom commented 1 year ago

Elevating the priority of this by labeling for PhET-iO meeting. I can't properly specify the StateSchema for my IOTypes in natural-selection until this is resolved. Using @ts-expect-error in multiple places in the meantime.

zepumph commented 1 year ago

More work on this today, but I couldn't crack it. https://github.com/Microsoft/TypeScript/issues/29023 https://stackoverflow.com/questions/24613955/is-there-a-type-in-typescript-for-anything-except-functions were helpful in telling me that typescript may not be able to assist us.

pixelzoom commented 1 year ago

... typescript may not be able to assist us.

It's not only assisting us, it's hindering us in this case.

If the _private pattern is going to be problematic with TypeScript, should we consider abandoning it? And brainstorm different patterns that would work with TypeScript?

Here's one idea: Flatten all StateObject types, since the nesting is presumably the problem. Treat fields in StateObject types that begin with "_" as private, and do not display them in Studio. For example:

type MyStateObject = {
  x: number;
  y: number;
-  _private: {
-    color: TColor
-  };
+  _color: TColor;
};

One disadvantage is that StateObject field names may not match core-type field names, so default behavior of IOType that relies on stateSchema may not work. (Maybe that's a problem with _private too? I don't know...)

Other ideas?

samreid commented 1 year ago

That is a reasonable proposal and it seems good. I don't know if there will be difficulty in implementing that and it would probably be good to investigate before alternatives. If it doesn't work out, other options could be like:

  private static getStateSchema( BunnyIO: IOType ): CompositeSchema {
    return {
      public: {
        father: NullableIO( ReferenceIO( BunnyIO ) ),
        age: NumberIO  
      },
      private: {
        hopDelta: Vector3.Vector3IO,
        hopStartPosition: Vector3.Vector3IO
      }
    };
  }

Or

  private static getStateSchema( BunnyIO: IOType ): CompositeSchema {
    return {
      father: NullableIO( ReferenceIO( BunnyIO ) ),
      age: NumberIO,
      hopDelta: private( Vector3.Vector3IO ),
      hopStartPosition: private( Vector3.Vector3IO )
    };
  }
samreid commented 1 year ago

Current progress, seems very promising:

```diff Subject: [PATCH] test --- Index: main/tandem/js/types/StateSchema.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/tandem/js/types/StateSchema.ts b/main/tandem/js/types/StateSchema.ts --- a/main/tandem/js/types/StateSchema.ts (revision a169c491c39eb76d716d130b9aca24d09602c1c7) +++ b/main/tandem/js/types/StateSchema.ts (date 1671059885683) @@ -94,37 +94,33 @@ public defaultApplyState( coreObject: T, stateObject: CompositeStateObjectType ): void { - const applyStateForLevel = ( schema: CompositeSchema, stateObjectLevel: GeneralStateObject ) => { - assert && assert( this.isComposite(), 'defaultApplyState from stateSchema only applies to composite stateSchemas' ); - for ( const stateKey in schema ) { - if ( schema.hasOwnProperty( stateKey ) ) { - if ( stateKey === '_private' ) { - applyStateForLevel( schema._private!, stateObjectLevel._private ); - } - else { + assert && assert( this.isComposite(), 'defaultApplyState from stateSchema only applies to composite stateSchemas' ); + for ( const stateKey in this.compositeSchema ) { + if ( this.compositeSchema.hasOwnProperty( stateKey ) ) { + assert && assert( stateObject.hasOwnProperty( stateKey ), `stateObject does not have expected schema key: ${stateKey}` ); + + assert && assert( stateKey !== '_private', 'please use _keyName instead of _private' ); - // The IOType for the key in the composite. - const schemaIOType = schema[ stateKey ]; - assert && assert( stateObjectLevel.hasOwnProperty( stateKey ), `stateObject does not have expected schema key: ${stateKey}` ); + // The IOType for the key in the composite. + const schemaIOType = this.compositeSchema[ stateKey ]; + + const objectKey = stateKey.startsWith( '_' ) ? stateKey.substring( 1 ) : stateKey; - // Using fromStateObject to deserialize sub-component - if ( schemaIOType.defaultDeserializationMethod === 'fromStateObject' ) { + // Using fromStateObject to deserialize sub-component + if ( schemaIOType.defaultDeserializationMethod === 'fromStateObject' ) { - // @ts-expect-error, I don't know how to tell typescript that we are accessing an expected key on the PhetioObject subtype. Likely there is no way with making things generic. - coreObject[ stateKey ] = schema[ stateKey ].fromStateObject( stateObjectLevel[ stateKey ] ); - } - else { - assert && assert( schemaIOType.defaultDeserializationMethod === 'applyState', 'unexpected deserialization method' ); + // @ts-expect-error, I don't know how to tell typescript that we are accessing an expected key on the PhetioObject subtype. Likely there is no way with making things generic. + coreObject[ objectKey ] = this.compositeSchema[ stateKey ].fromStateObject( stateObject[ objectKey ] ); + } + else { + assert && assert( schemaIOType.defaultDeserializationMethod === 'applyState', 'unexpected deserialization method' ); - // Using applyState to deserialize sub-component - // @ts-expect-error, I don't know how to tell typescript that we are accessing an expected key on the PhetioObject subtype. Likely there is no way with making things generic. - schema[ stateKey ].applyState( coreObject[ stateKey ], stateObjectLevel[ stateKey ] ); - } - } - } - } - }; - applyStateForLevel( this.compositeSchema!, stateObject ); + // Using applyState to deserialize sub-component + // @ts-expect-error, I don't know how to tell typescript that we are accessing an expected key on the PhetioObject subtype. Likely there is no way with making things generic. + this.compositeSchema[ stateKey ].applyState( coreObject[ objectKey ], stateObject[ objectKey ] ); + } + } + } } public defaultToStateObject( coreObject: T ): StateType { @@ -163,7 +159,6 @@ return !!this.compositeSchema; } - /** * Check if a given stateObject is as valid as can be determined by this StateSchema. Will return null if valid, but * needs more checking up and down the hierarchy. Index: main/natural-selection/js/common/model/Bunny.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/natural-selection/js/common/model/Bunny.ts b/main/natural-selection/js/common/model/Bunny.ts --- a/main/natural-selection/js/common/model/Bunny.ts (revision d16acfddcec952044cacbbef1c22489007278119) +++ b/main/natural-selection/js/common/model/Bunny.ts (date 1671059422290) @@ -53,14 +53,12 @@ generation: number; isAlive: boolean; age: number; - _private: { - restTime: number; - hopTime: number; - cumulativeRestTime: number; - cumulativeHopTime: number; - hopDelta: Vector3StateObject; - hopStartPosition: Vector3StateObject; - }; + _restTime: number; + _hopTime: number; + _cumulativeRestTime: number; + _cumulativeHopTime: number; + _hopDelta: Vector3StateObject; + hopStartPosition: Vector3StateObject; }; export default class Bunny extends Organism { ```
samreid commented 1 year ago

I feel like this issue should be addressed as part of an iteration goal. So I'll self-unassign and add it to the proposed backlog issues for next iteration.

samreid commented 1 year ago

This will likely need a migration rule since BunnyIO state currently looks like this:

```ts "naturalSelection.introScreen.model.bunnyCollection.bunnyGroup.bunny_0": { "father": null, "mother": null, "generation": 0, "isAlive": true, "age": 0, "_private": { "restTime": 1.2822408187030734, "hopTime": 0.2914619109330386, "cumulativeRestTime": 0.15000000000000002, "cumulativeHopTime": 0, "hopDelta": { "x": 16.879200867984476, "y": 47.336095635726515, "z": 2.7777672967662856 }, "hopStartPosition": { "x": -140.11502577110898, "y": -50.6389141306228, "z": 224.0416288040658 } } } ```
samreid commented 1 year ago

More progress but not running yet. Should I be in a branch?

```diff Subject: [PATCH] Fix typo, see https://github.com/phetsims/scenery/issues/665 --- Index: main/tandem/js/types/IOType.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/tandem/js/types/IOType.ts b/main/tandem/js/types/IOType.ts --- a/main/tandem/js/types/IOType.ts (revision d5d7b1b4c3f3e579a4bebdf1d8370280e811b243) +++ b/main/tandem/js/types/IOType.ts (date 1679875056675) @@ -394,14 +394,14 @@ * @param privateSchemaKeys=[] * @returns if the stateObject is valid or not. */ - public isStateObjectValid( stateObject: StateType, toAssert = false, publicSchemaKeys: string[] = [], privateSchemaKeys: string[] = [] ): boolean { + public isStateObjectValid( stateObject: StateType, toAssert = false ): boolean { // Set to false when invalid let valid = true; // make sure the stateObject has everything the schema requires and nothing more if ( this.stateSchema ) { - const validSoFar = this.stateSchema.checkStateObjectValid( stateObject, toAssert, publicSchemaKeys, privateSchemaKeys ); + const validSoFar = this.stateSchema.checkStateObjectValid( stateObject, toAssert ); // null as a marker to keep checking up the hierarchy, otherwise we reached our based case because the stateSchema was a value, not a composite if ( validSoFar !== null ) { @@ -410,27 +410,23 @@ } if ( this.supertype ) { - return valid && this.supertype.isStateObjectValid( stateObject, toAssert, publicSchemaKeys, privateSchemaKeys ); + return valid && this.supertype.isStateObjectValid( stateObject, toAssert ); } // When we reach the root, make sure there isn't anything in the stateObject that isn't described by a schema if ( !this.supertype && stateObject && typeof stateObject !== 'string' && !Array.isArray( stateObject ) ) { - const check = ( type: 'public' | 'private', key: string ) => { - const keys = type === 'public' ? publicSchemaKeys : privateSchemaKeys; + const check = ( key: string ) => { + const keys = Object.keys( this.stateSchema ); const keyValid = keys.includes( key ); if ( !keyValid ) { valid = false; } - assert && toAssert && assert( keyValid, `stateObject provided a ${type} key that is not in the schema: ${key}` ); + assert && toAssert && assert( keyValid, `stateObject provided a key that is not in the schema: ${key}` ); }; - // Visit the public state - Object.keys( stateObject ).filter( key => key !== '_private' ).forEach( key => check( 'public', key ) ); - - // Visit the private state, if any - // @ts-expect-error stateObjects can take a variety of forms, they don't have to be a record, thus, it is challenging to be graceful to a `_private` key - stateObject._private && Object.keys( stateObject._private ).forEach( key => check( 'private', key ) ); + // Visit the state + Object.keys( stateObject ).forEach( key => check( key ) ); return valid; } Index: main/phet-io/doc/phet-io-instrumentation-technical-guide.md IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/phet-io/doc/phet-io-instrumentation-technical-guide.md b/main/phet-io/doc/phet-io-instrumentation-technical-guide.md --- a/main/phet-io/doc/phet-io-instrumentation-technical-guide.md (revision b25e095281b28e0e18e7f01b4c25f262057dc014) +++ b/main/phet-io/doc/phet-io-instrumentation-technical-guide.md (date 1679872720075) @@ -546,9 +546,8 @@ PhET-iO element publicly to the client. For example, the state for an AXON/Property has a `value` field. Though this is used by the state engine to restore state, it has been designed to also be available to the client as the current value of the Property. If there is state for a PhET-iO element that is needed to support save load, but shouldn't "leak" -out for clients to view and use (often because these are implementation details), then, by convention, they should be -nested under the `_private` key in the state object. This will ensure that they are not displayed publicly (in -Studio/etc). +out for clients to view and use (often because these are implementation details), then, by convention, they should have +an underscore prefix, like `_value`. This will ensure that they are not displayed publicly (in Studio/etc). #### Strategies for implementing save and load Index: main/wave-on-a-string/js/wave-on-a-string/model/WOASModel.js IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/wave-on-a-string/js/wave-on-a-string/model/WOASModel.js b/main/wave-on-a-string/js/wave-on-a-string/model/WOASModel.js --- a/main/wave-on-a-string/js/wave-on-a-string/model/WOASModel.js (revision cc3748249eb76864632a2c57789fe8aaf9661eae) +++ b/main/wave-on-a-string/js/wave-on-a-string/model/WOASModel.js (date 1679872920019) @@ -518,29 +518,25 @@ valueType: WOASModel, documentation: 'The main model for Wave on a String', toStateObject: model => ( { - _private: { - yDraw: Float64ArrayIO.toStateObject( model.yDraw ), - yNow: Float64ArrayIO.toStateObject( model.yNow ), - yLast: Float64ArrayIO.toStateObject( model.yLast ), - yNext: Float64ArrayIO.toStateObject( model.yNext ) - } + yDraw: Float64ArrayIO.toStateObject( model.yDraw ), + yNow: Float64ArrayIO.toStateObject( model.yNow ), + yLast: Float64ArrayIO.toStateObject( model.yLast ), + yNext: Float64ArrayIO.toStateObject( model.yNext ) } ), stateSchema: { - _private: { - yDraw: Float64ArrayIO, - yNow: Float64ArrayIO, - yLast: Float64ArrayIO, - yNext: Float64ArrayIO - } + _yDraw: Float64ArrayIO, + _yNow: Float64ArrayIO, + _yLast: Float64ArrayIO, + _yNext: Float64ArrayIO }, applyState: ( model, stateObject ) => { // We make an assumption about Float64ArrayIO's serialization here, so that we don't create temporary garbage // Float64Arrays. Instead we set the array values directly. - model.yDraw.set( stateObject._private.yDraw ); - model.yNow.set( stateObject._private.yNow ); - model.yLast.set( stateObject._private.yLast ); - model.yNext.set( stateObject._private.yNext ); + model.yDraw.set( stateObject.yDraw ); + model.yNow.set( stateObject.yNow ); + model.yLast.set( stateObject.yLast ); + model.yNext.set( stateObject.yNext ); model.yNowChangedEmitter.emit(); } Index: main/studio/js/PhetioElementView.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/studio/js/PhetioElementView.ts b/main/studio/js/PhetioElementView.ts --- a/main/studio/js/PhetioElementView.ts (revision 98ce514a39ca3c748149e7f41d0da1038f21f703) +++ b/main/studio/js/PhetioElementView.ts (date 1679874395465) @@ -451,17 +451,12 @@ }; // Recursively remove "private" state from the provided state object. -const omitPrivateState = ( object: { _private?: object } ) => { +const omitPrivateState = ( object: IntentionalAny ) => { _.forIn( object, ( value, key ) => { // Nested private data should not be displayed in state - if ( key === '_private' ) { - delete object._private; - } - else if ( value instanceof Object ) { - // no need to recurse inside the private data, since it has been excluded - - omitPrivateState( value ); + if ( key.startsWith( '_' ) ) { + delete object[ key ]; } } ); }; Index: main/tandem/js/types/StateSchema.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/tandem/js/types/StateSchema.ts b/main/tandem/js/types/StateSchema.ts --- a/main/tandem/js/types/StateSchema.ts (revision d5d7b1b4c3f3e579a4bebdf1d8370280e811b243) +++ b/main/tandem/js/types/StateSchema.ts (date 1679874909547) @@ -20,17 +20,11 @@ import IOType from './IOType.js'; import IntentionalAny from '../../../phet-core/js/types/IntentionalAny.js'; -export type CompositeSchema = Record & { - _private?: CompositeSchema; -}; +export type CompositeSchema = Record; -type CompositeSchemaAPI = Record & { - _private?: Record; -}; +type CompositeSchemaAPI = Record; -export type CompositeStateObjectType = Record & { - _private?: Record; -}; +export type CompositeStateObjectType = Record; type StateSchemaOptions = { displayString?: string; @@ -64,67 +58,37 @@ this.validator = options.validator; this.compositeSchema = options.compositeSchema; - - assert && this.validateStateSchema( this.compositeSchema ); - } - - - /** - * Make sure that a composite state schema is of the correct form. Each value in the object should be an IOType - * Only useful if assert is called. - */ - private validateStateSchema( stateSchema: CompositeSchema | null ): void { - if ( assert && this.isComposite() ) { - - for ( const stateSchemaKey in stateSchema ) { - if ( stateSchema.hasOwnProperty( stateSchemaKey ) ) { - - - if ( stateSchemaKey === '_private' ) { - - // By putting the assignment in this statement, typescript knows the value as a CompositeSchema - const stateSchemaValue = stateSchema[ stateSchemaKey ]; - assert && assert( stateSchemaValue, 'should not be undefined' ); - this.validateStateSchema( stateSchemaValue! ); - } - } - } - } } public defaultApplyState( coreObject: T, stateObject: CompositeStateObjectType ): void { - const applyStateForLevel = ( schema: CompositeSchema, stateObjectLevel: GeneralStateObject ) => { - assert && assert( this.isComposite(), 'defaultApplyState from stateSchema only applies to composite stateSchemas' ); - for ( const stateKey in schema ) { - if ( schema.hasOwnProperty( stateKey ) ) { - if ( stateKey === '_private' ) { - applyStateForLevel( schema._private!, stateObjectLevel._private ); - } - else { + assert && assert( this.isComposite(), 'defaultApplyState from stateSchema only applies to composite stateSchemas' ); + for ( const stateKey in this.compositeSchema ) { + if ( this.compositeSchema.hasOwnProperty( stateKey ) ) { + assert && assert( stateObject.hasOwnProperty( stateKey ), `stateObject does not have expected schema key: ${stateKey}` ); + + assert && assert( stateKey !== '_private', 'please use _keyName instead of _private' ); - // The IOType for the key in the composite. - const schemaIOType = schema[ stateKey ]; - assert && assert( stateObjectLevel.hasOwnProperty( stateKey ), `stateObject does not have expected schema key: ${stateKey}` ); + // The IOType for the key in the composite. + const schemaIOType = this.compositeSchema[ stateKey ]; + + const objectKey = stateKey.startsWith( '_' ) ? stateKey.substring( 1 ) : stateKey; - // Using fromStateObject to deserialize sub-component - if ( schemaIOType.defaultDeserializationMethod === 'fromStateObject' ) { + // Using fromStateObject to deserialize sub-component + if ( schemaIOType.defaultDeserializationMethod === 'fromStateObject' ) { - // @ts-expect-error, I don't know how to tell typescript that we are accessing an expected key on the PhetioObject subtype. Likely there is no way with making things generic. - coreObject[ stateKey ] = schema[ stateKey ].fromStateObject( stateObjectLevel[ stateKey ] ); - } - else { - assert && assert( schemaIOType.defaultDeserializationMethod === 'applyState', 'unexpected deserialization method' ); + // @ts-expect-error, I don't know how to tell typescript that we are accessing an expected key on the PhetioObject subtype. Likely there is no way with making things generic. + coreObject[ objectKey ] = this.compositeSchema[ stateKey ].fromStateObject( stateObject[ objectKey ] ); + } + else { + assert && assert( schemaIOType.defaultDeserializationMethod === 'applyState', 'unexpected deserialization method' ); - // Using applyState to deserialize sub-component - // @ts-expect-error, I don't know how to tell typescript that we are accessing an expected key on the PhetioObject subtype. Likely there is no way with making things generic. - schema[ stateKey ].applyState( coreObject[ stateKey ], stateObjectLevel[ stateKey ] ); - } - } - } - } - }; - applyStateForLevel( this.compositeSchema!, stateObject ); + // Using applyState to deserialize sub-component + // @ts-expect-error, I don't know how to tell typescript that we are accessing an expected key on the PhetioObject subtype. Likely there is no way with making things generic. + this.compositeSchema[ stateKey ].applyState( coreObject[ objectKey ], stateObject[ objectKey ] ); + } + } + } } public defaultToStateObject( coreObject: T ): StateType { @@ -135,20 +99,16 @@ const stateObject = {} as StateType; for ( const stateKey in schema ) { if ( schema.hasOwnProperty( stateKey ) ) { - if ( stateKey === '_private' ) { - - // @ts-expect-error Still working on _private in https://github.com/phetsims/tandem/issues/282 - stateObject._private = toStateObjectForSchemaLevel( schema._private ); - } - else { - // @ts-expect-error I guess we need to support schemas outside of composite here, or tell how to avoid that, https://github.com/phetsims/tandem/issues/261 - assert && assert( coreObject.hasOwnProperty( stateKey ), - `cannot get state because coreObject does not have expected schema key: ${stateKey}` ); + // @ts-expect-error I guess we need to support schemas outside of composite here, or tell how to avoid that, https://github.com/phetsims/tandem/issues/261 + assert && assert( coreObject.hasOwnProperty( stateKey ), + `cannot get state because coreObject does not have expected schema key: ${stateKey}` ); - // @ts-expect-error https://github.com/phetsims/tandem/issues/261 - stateObject[ stateKey ] = schema[ stateKey ].toStateObject( coreObject[ stateKey ] ); - } + // Trim the '_' if any + const coreObjectAccessor = stateKey.startsWith( '_' ) ? stateKey.substring( 1 ) : stateKey; + + // @ts-expect-error https://github.com/phetsims/tandem/issues/261 + stateObject[ stateKey ] = schema[ stateKey ].toStateObject( coreObject[ coreObjectAccessor ] ); } } return stateObject; @@ -163,7 +123,6 @@ return !!this.compositeSchema; } - /** * Check if a given stateObject is as valid as can be determined by this StateSchema. Will return null if valid, but * needs more checking up and down the hierarchy. @@ -174,31 +133,29 @@ * @param privateSchemaKeys - to be populated with any private keys this StateSchema is responsible for * @returns boolean if validity can be checked, null if valid, but next in the hierarchy is needed */ - public checkStateObjectValid( stateObject: StateType, toAssert: boolean, publicSchemaKeys: string[], privateSchemaKeys: string[] ): boolean | null { + public checkStateObjectValid( stateObject: StateType, toAssert: boolean ): boolean | null { if ( this.isComposite() ) { const compositeStateObject = stateObject as CompositeStateObjectType; const schema = this.compositeSchema!; let valid = null; - const checkLevel = ( schemaLevel: CompositeSchema, objectLevel: GeneralStateObject, keyList: string[], exclude: string | null ) => { + const checkLevel = ( schemaLevel: CompositeSchema, objectLevel: GeneralStateObject ) => { if ( !objectLevel ) { valid = false; assert && toAssert && assert( false, 'There was no stateObject, but there was a state schema saying there should be', schemaLevel ); return; } - Object.keys( schemaLevel ).filter( ( k: string ) => k !== exclude ).forEach( key => { + Object.keys( schemaLevel ).forEach( key => { const validKey = objectLevel.hasOwnProperty( key ); if ( !validKey ) { valid = false; } assert && toAssert && assert( validKey, `${key} in state schema but not in the state object` ); schemaLevel[ key ].validateStateObject( objectLevel[ key ] ); - keyList.push( key ); } ); }; - checkLevel( schema, compositeStateObject, publicSchemaKeys, '_private' ); - schema._private && checkLevel( schema._private, compositeStateObject._private!, privateSchemaKeys, null ); + checkLevel( schema, compositeStateObject ); return valid; } else { @@ -218,18 +175,11 @@ public getRelatedTypes(): IOType[] { const relatedTypes: IOType[] = []; - // to support IOTypes from public and private state - const getRelatedStateTypeForLevel = ( stateSchema: CompositeSchema ) => { - Object.keys( stateSchema ).forEach( stateSchemaKey => { - - // Support keywords in state like "private" - stateSchema[ stateSchemaKey ] instanceof IOType && relatedTypes.push( stateSchema[ stateSchemaKey ] ); - } ); - }; - if ( this.compositeSchema ) { - getRelatedStateTypeForLevel( this.compositeSchema ); - this.compositeSchema._private && getRelatedStateTypeForLevel( this.compositeSchema._private ); + Object.keys( this.compositeSchema ).forEach( stateSchemaKey => { + + this.compositeSchema![ stateSchemaKey ] instanceof IOType && relatedTypes.push( this.compositeSchema![ stateSchemaKey ] ); + } ); } return relatedTypes; } @@ -241,11 +191,7 @@ */ public getStateSchemaAPI(): string | CompositeSchemaAPI { if ( this.isComposite() ) { - const stateSchemaAPI = _.mapValues( this.compositeSchema, value => value.typeName ) as CompositeSchemaAPI; - if ( this.compositeSchema!._private ) { - stateSchemaAPI._private = _.mapValues( this.compositeSchema!._private, value => value.typeName ); - } - return stateSchemaAPI; + return _.mapValues( this.compositeSchema, value => value.typeName ) as CompositeSchemaAPI; } else { return this.displayString; Index: main/natural-selection/js/common/model/Bunny.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/natural-selection/js/common/model/Bunny.ts b/main/natural-selection/js/common/model/Bunny.ts --- a/main/natural-selection/js/common/model/Bunny.ts (revision 85a588353606e96e2d90443f51a7a6a99d1946ab) +++ b/main/natural-selection/js/common/model/Bunny.ts (date 1679872920014) @@ -53,14 +53,12 @@ generation: number; isAlive: boolean; age: number; - _private: { - restTime: number; - hopTime: number; - cumulativeRestTime: number; - cumulativeHopTime: number; - hopDelta: Vector3StateObject; - hopStartPosition: Vector3StateObject; - }; + _restTime: number; + _hopTime: number; + _cumulativeRestTime: number; + _cumulativeHopTime: number; + _hopDelta: Vector3StateObject; + _hopStartPosition: Vector3StateObject; }; export default class Bunny extends Organism { @@ -395,15 +393,12 @@ // genotype and phenotype are stateful and will be serialized automatically. // private fields, will not be shown in Studio - _private: { - // @ts-expect-error https://github.com/phetsims/tandem/issues/282 TypeScript support for _private - restTime: NumberIO, - hopTime: NumberIO, - cumulativeRestTime: NumberIO, - cumulativeHopTime: NumberIO, - hopDelta: Vector3.Vector3IO, - hopStartPosition: Vector3.Vector3IO - } + _restTime: NumberIO, + _hopTime: NumberIO, + _cumulativeRestTime: NumberIO, + _cumulativeHopTime: NumberIO, + _hopDelta: Vector3.Vector3IO, + _hopStartPosition: Vector3.Vector3IO }; } Index: main/natural-selection/js/common/model/Wolf.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/natural-selection/js/common/model/Wolf.ts b/main/natural-selection/js/common/model/Wolf.ts --- a/main/natural-selection/js/common/model/Wolf.ts (revision 85a588353606e96e2d90443f51a7a6a99d1946ab) +++ b/main/natural-selection/js/common/model/Wolf.ts (date 1679874355225) @@ -27,9 +27,7 @@ const WOLF_SPEED_RANGE = new Range( 125, 200 ); type WolfStateObject = { - _private: { - speed: number; - }; + _speed: number; }; export default class Wolf extends Organism { @@ -117,9 +115,7 @@ private toStateObject(): WolfStateObject { return { // position and xDirection are handled by super Organism instrumented Properties - _private: { - speed: this._speed - } + _speed: this._speed }; } @@ -127,7 +123,7 @@ * Restores Wolf state after instantiation. */ private applyState( stateObject: WolfStateObject ): void { - this._speed = stateObject._private.speed; + this._speed = stateObject._speed; } /** @@ -138,13 +134,10 @@ public static readonly WolfIO = new IOType( 'WolfIO', { valueType: Wolf, - // @ts-expect-error https://github.com/phetsims/tandem/issues/282 TypeScript support for _private stateSchema: { // private fields, will not be shown in Studio - _private: { - speed: NumberIO - } + _speed: NumberIO }, toStateObject: wolf => wolf.toStateObject(), applyState: ( wolf, stateObject ) => wolf.applyState( stateObject ) Index: main/states-of-matter/js/common/model/MultipleParticleModel.js IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/states-of-matter/js/common/model/MultipleParticleModel.js b/main/states-of-matter/js/common/model/MultipleParticleModel.js --- a/main/states-of-matter/js/common/model/MultipleParticleModel.js (revision da663e80b553643694da2d4038806a1c99e29d3b) +++ b/main/states-of-matter/js/common/model/MultipleParticleModel.js (date 1679872870437) @@ -1366,18 +1366,16 @@ */ toStateObject() { return { - _private: { // to indicate that it is needed for state, but shouldn't be shown in Studio - substance: EnumerationIO( SubstanceType ).toStateObject( this.substanceProperty.value ), - isExploded: this.isExplodedProperty.value, - containerHeight: this.containerHeightProperty.value, - gravitationalAcceleration: this.gravitationalAcceleration, - normalizedLidVelocityY: this.normalizedLidVelocityY, - heatingCoolingAmount: this.heatingCoolingAmountProperty.value, - moleculeDataSet: MoleculeForceAndMotionDataSet.MoleculeForceAndMotionDataSetIO.toStateObject( this.moleculeDataSet ), - isoKineticThermostatState: this.isoKineticThermostat.toStateObject(), - andersenThermostatState: this.andersenThermostat.toStateObject(), - moleculeForcesAndMotionCalculatorPressure: this.moleculeForceAndMotionCalculator.pressureProperty.value - } + _substance: EnumerationIO( SubstanceType ).toStateObject( this.substanceProperty.value ), + _isExploded: this.isExplodedProperty.value, + _containerHeight: this.containerHeightProperty.value, + _gravitationalAcceleration: this.gravitationalAcceleration, + _normalizedLidVelocityY: this.normalizedLidVelocityY, + _heatingCoolingAmount: this.heatingCoolingAmountProperty.value, + _moleculeDataSet: MoleculeForceAndMotionDataSet.MoleculeForceAndMotionDataSetIO.toStateObject( this.moleculeDataSet ), + _isoKineticThermostatState: this.isoKineticThermostat.toStateObject(), + _andersenThermostatState: this.andersenThermostat.toStateObject(), + _moleculeForcesAndMotionCalculatorPressure: this.moleculeForceAndMotionCalculator.pressureProperty.value }; } @@ -1392,22 +1390,22 @@ // Setting the substance initializes a bunch of model parameters, so this is done first, then other items that may // have been affected are set. - this.substanceProperty.set( EnumerationIO( SubstanceType ).fromStateObject( stateObject._private.substance ) ); + this.substanceProperty.set( EnumerationIO( SubstanceType ).fromStateObject( stateObject._substance ) ); // Set properties that may have been updated by setting the substance. - this.isExplodedProperty.set( stateObject._private.isExploded ); - this.containerHeightProperty.set( stateObject._private.containerHeight ); - this.heatingCoolingAmountProperty.set( stateObject._private.heatingCoolingAmount ); - this.gravitationalAcceleration = stateObject._private.gravitationalAcceleration; - this.normalizedLidVelocityY = stateObject._private.normalizedLidVelocityY; - this.isoKineticThermostat.setState( stateObject._private.isoKineticThermostatState ); - this.andersenThermostat.setState( stateObject._private.andersenThermostatState ); + this.isExplodedProperty.set( stateObject._isExploded ); + this.containerHeightProperty.set( stateObject._containerHeight ); + this.heatingCoolingAmountProperty.set( stateObject._heatingCoolingAmount ); + this.gravitationalAcceleration = stateObject._gravitationalAcceleration; + this.normalizedLidVelocityY = stateObject._normalizedLidVelocityY; + this.isoKineticThermostat.setState( stateObject._isoKineticThermostatState ); + this.andersenThermostat.setState( stateObject._andersenThermostatState ); // Set the molecule data set. This includes all the positions, velocities, etc. for the particles. - this.moleculeDataSet.setState( stateObject._private.moleculeDataSet ); + this.moleculeDataSet.setState( stateObject._moleculeDataSet ); // Preset the pressure in the accumulator that tracks it so that it doesn't have to start from zero. - this.moleculeForceAndMotionCalculator.presetPressure( stateObject._private.moleculeForcesAndMotionCalculatorPressure ); + this.moleculeForceAndMotionCalculator.presetPressure( stateObject._moleculeForcesAndMotionCalculatorPressure ); // Make sure that we have the right number of scaled (i.e. non-normalized) atoms. const numberOfNormalizedMolecules = this.moleculeDataSet.numberOfMolecules; @@ -1441,18 +1439,16 @@ toStateObject: multipleParticleModel => multipleParticleModel.toStateObject(), applyState: ( multipleParticleModel, state ) => multipleParticleModel.applyState( state ), stateSchema: { - _private: { - substance: EnumerationIO( SubstanceType ), - isExploded: BooleanIO, - containerHeight: NumberIO, - gravitationalAcceleration: NumberIO, - normalizedLidVelocityY: NumberIO, - heatingCoolingAmount: NumberIO, - moleculeDataSet: MoleculeForceAndMotionDataSet.MoleculeForceAndMotionDataSetIO, - isoKineticThermostatState: IsokineticThermostat.IsoKineticThermostatIO, - andersenThermostatState: AndersenThermostat.AndersenThermostatIO, - moleculeForcesAndMotionCalculatorPressure: NumberIO - } + _substance: EnumerationIO( SubstanceType ), + _isExploded: BooleanIO, + _containerHeight: NumberIO, + _gravitationalAcceleration: NumberIO, + _normalizedLidVelocityY: NumberIO, + _heatingCoolingAmount: NumberIO, + _moleculeDataSet: MoleculeForceAndMotionDataSet.MoleculeForceAndMotionDataSetIO, + _isoKineticThermostatState: IsokineticThermostat.IsoKineticThermostatIO, + _andersenThermostatState: AndersenThermostat.AndersenThermostatIO, + _moleculeForcesAndMotionCalculatorPressure: NumberIO } } ); Index: main/proportion-playground/js/common/model/paint/PaintChoice.js IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/proportion-playground/js/common/model/paint/PaintChoice.js b/main/proportion-playground/js/common/model/paint/PaintChoice.js --- a/main/proportion-playground/js/common/model/paint/PaintChoice.js (revision 03b9b292f1918da93149b92fe7ad0ff0e64fecf3) +++ b/main/proportion-playground/js/common/model/paint/PaintChoice.js (date 1679872870440) @@ -135,18 +135,14 @@ documentation: 'A combination of a left and right effective color', toStateObject( paintChoice ) { return { - _private: { - choice: ( paintChoice === PaintChoice.BLUE_YELLOW ) ? 'BLUE_YELLOW' : ( paintChoice === PaintChoice.RED_YELLOW ? 'RED_YELLOW' : 'BLACK_WHITE' ) - } + _choice: ( paintChoice === PaintChoice.BLUE_YELLOW ) ? 'BLUE_YELLOW' : ( paintChoice === PaintChoice.RED_YELLOW ? 'RED_YELLOW' : 'BLACK_WHITE' ) }; }, stateSchema: { - _private: { - choice: StringIO - } + _choice: StringIO }, fromStateObject( stateObject ) { - return PaintChoice[ stateObject._private.choice ]; + return PaintChoice[ stateObject._choice ]; } } ); PaintChoice.PaintChoiceIO = PaintChoiceIO; ```
zepumph commented 1 year ago

@samreid and I got to a commit point, but will hold out until a couple of RC shas are taken for high priority sims:

``` Subject: [PATCH] simplify two statements into one, https://github.com/phetsims/number-suite-common/issues/29 --- Index: studio/js/PhetioElementView.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/studio/js/PhetioElementView.ts b/studio/js/PhetioElementView.ts --- a/studio/js/PhetioElementView.ts (revision 98ce514a39ca3c748149e7f41d0da1038f21f703) +++ b/studio/js/PhetioElementView.ts (date 1679955260380) @@ -451,17 +451,12 @@ }; // Recursively remove "private" state from the provided state object. -const omitPrivateState = ( object: { _private?: object } ) => { +const omitPrivateState = ( object: IntentionalAny ) => { _.forIn( object, ( value, key ) => { // Nested private data should not be displayed in state - if ( key === '_private' ) { - delete object._private; - } - else if ( value instanceof Object ) { - // no need to recurse inside the private data, since it has been excluded - - omitPrivateState( value ); + if ( key.startsWith( '_' ) ) { + delete object[ key ]; } } ); }; @@ -536,7 +531,7 @@ // Children (if any) to add below the space const detailsContainers: DetailsContainer[] = []; - // Objects can be serialized whether or not they are phetioState: true + // Objects can be serialized whether they are phetioState: true if ( phetioElement.initialValue && Object.keys( phetioElement.initialValue ).length > 0 ) { omitPrivateState( phetioElement.initialValue ); Index: phet-io/doc/phet-io-instrumentation-technical-guide.md IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/phet-io/doc/phet-io-instrumentation-technical-guide.md b/phet-io/doc/phet-io-instrumentation-technical-guide.md --- a/phet-io/doc/phet-io-instrumentation-technical-guide.md (revision b25e095281b28e0e18e7f01b4c25f262057dc014) +++ b/phet-io/doc/phet-io-instrumentation-technical-guide.md (date 1679951475578) @@ -546,9 +546,8 @@ PhET-iO element publicly to the client. For example, the state for an AXON/Property has a `value` field. Though this is used by the state engine to restore state, it has been designed to also be available to the client as the current value of the Property. If there is state for a PhET-iO element that is needed to support save load, but shouldn't "leak" -out for clients to view and use (often because these are implementation details), then, by convention, they should be -nested under the `_private` key in the state object. This will ensure that they are not displayed publicly (in -Studio/etc). +out for clients to view and use (often because these are implementation details), then, by convention, they should have +an underscore prefix, like `_value`. This will ensure that they are not displayed publicly (in Studio/etc). #### Strategies for implementing save and load Index: wave-on-a-string/js/wave-on-a-string/model/WOASModel.js IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/wave-on-a-string/js/wave-on-a-string/model/WOASModel.js b/wave-on-a-string/js/wave-on-a-string/model/WOASModel.js --- a/wave-on-a-string/js/wave-on-a-string/model/WOASModel.js (revision cc3748249eb76864632a2c57789fe8aaf9661eae) +++ b/wave-on-a-string/js/wave-on-a-string/model/WOASModel.js (date 1679951475623) @@ -518,29 +518,25 @@ valueType: WOASModel, documentation: 'The main model for Wave on a String', toStateObject: model => ( { - _private: { - yDraw: Float64ArrayIO.toStateObject( model.yDraw ), - yNow: Float64ArrayIO.toStateObject( model.yNow ), - yLast: Float64ArrayIO.toStateObject( model.yLast ), - yNext: Float64ArrayIO.toStateObject( model.yNext ) - } + yDraw: Float64ArrayIO.toStateObject( model.yDraw ), + yNow: Float64ArrayIO.toStateObject( model.yNow ), + yLast: Float64ArrayIO.toStateObject( model.yLast ), + yNext: Float64ArrayIO.toStateObject( model.yNext ) } ), stateSchema: { - _private: { - yDraw: Float64ArrayIO, - yNow: Float64ArrayIO, - yLast: Float64ArrayIO, - yNext: Float64ArrayIO - } + _yDraw: Float64ArrayIO, + _yNow: Float64ArrayIO, + _yLast: Float64ArrayIO, + _yNext: Float64ArrayIO }, applyState: ( model, stateObject ) => { // We make an assumption about Float64ArrayIO's serialization here, so that we don't create temporary garbage // Float64Arrays. Instead we set the array values directly. - model.yDraw.set( stateObject._private.yDraw ); - model.yNow.set( stateObject._private.yNow ); - model.yLast.set( stateObject._private.yLast ); - model.yNext.set( stateObject._private.yNext ); + model.yDraw.set( stateObject.yDraw ); + model.yNow.set( stateObject.yNow ); + model.yLast.set( stateObject.yLast ); + model.yNext.set( stateObject.yNext ); model.yNowChangedEmitter.emit(); } Index: natural-selection/js/common/model/Wolf.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/natural-selection/js/common/model/Wolf.ts b/natural-selection/js/common/model/Wolf.ts --- a/natural-selection/js/common/model/Wolf.ts (revision 85a588353606e96e2d90443f51a7a6a99d1946ab) +++ b/natural-selection/js/common/model/Wolf.ts (date 1679951475557) @@ -27,9 +27,7 @@ const WOLF_SPEED_RANGE = new Range( 125, 200 ); type WolfStateObject = { - _private: { - speed: number; - }; + _speed: number; }; export default class Wolf extends Organism { @@ -117,9 +115,7 @@ private toStateObject(): WolfStateObject { return { // position and xDirection are handled by super Organism instrumented Properties - _private: { - speed: this._speed - } + _speed: this._speed }; } @@ -127,7 +123,7 @@ * Restores Wolf state after instantiation. */ private applyState( stateObject: WolfStateObject ): void { - this._speed = stateObject._private.speed; + this._speed = stateObject._speed; } /** @@ -138,13 +134,10 @@ public static readonly WolfIO = new IOType( 'WolfIO', { valueType: Wolf, - // @ts-expect-error https://github.com/phetsims/tandem/issues/282 TypeScript support for _private stateSchema: { // private fields, will not be shown in Studio - _private: { - speed: NumberIO - } + _speed: NumberIO }, toStateObject: wolf => wolf.toStateObject(), applyState: ( wolf, stateObject ) => wolf.applyState( stateObject ) Index: natural-selection/js/common/model/Bunny.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/natural-selection/js/common/model/Bunny.ts b/natural-selection/js/common/model/Bunny.ts --- a/natural-selection/js/common/model/Bunny.ts (revision 85a588353606e96e2d90443f51a7a6a99d1946ab) +++ b/natural-selection/js/common/model/Bunny.ts (date 1679951475551) @@ -53,14 +53,12 @@ generation: number; isAlive: boolean; age: number; - _private: { - restTime: number; - hopTime: number; - cumulativeRestTime: number; - cumulativeHopTime: number; - hopDelta: Vector3StateObject; - hopStartPosition: Vector3StateObject; - }; + _restTime: number; + _hopTime: number; + _cumulativeRestTime: number; + _cumulativeHopTime: number; + _hopDelta: Vector3StateObject; + _hopStartPosition: Vector3StateObject; }; export default class Bunny extends Organism { @@ -395,15 +393,12 @@ // genotype and phenotype are stateful and will be serialized automatically. // private fields, will not be shown in Studio - _private: { - // @ts-expect-error https://github.com/phetsims/tandem/issues/282 TypeScript support for _private - restTime: NumberIO, - hopTime: NumberIO, - cumulativeRestTime: NumberIO, - cumulativeHopTime: NumberIO, - hopDelta: Vector3.Vector3IO, - hopStartPosition: Vector3.Vector3IO - } + _restTime: NumberIO, + _hopTime: NumberIO, + _cumulativeRestTime: NumberIO, + _cumulativeHopTime: NumberIO, + _hopDelta: Vector3.Vector3IO, + _hopStartPosition: Vector3.Vector3IO }; } Index: tandem/js/types/IOType.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/tandem/js/types/IOType.ts b/tandem/js/types/IOType.ts --- a/tandem/js/types/IOType.ts (revision 9e04ba7e40acc6c7952d9dff599c1201783667bb) +++ b/tandem/js/types/IOType.ts (date 1679954731695) @@ -12,7 +12,7 @@ import Validation, { Validator } from '../../../axon/js/Validation.js'; import optionize from '../../../phet-core/js/optionize.js'; import PhetioConstants from '../PhetioConstants.js'; -import TandemConstants, { PhetioObjectMetadata } from '../TandemConstants.js'; +import TandemConstants, { IOTypeName, PhetioObjectMetadata } from '../TandemConstants.js'; import tandemNamespace from '../tandemNamespace.js'; import StateSchema, { CompositeSchema, CompositeStateObjectType } from './StateSchema.js'; import type PhetioObject from '../PhetioObject.js'; @@ -25,7 +25,7 @@ /** * Estimate the core type name from a given IO Type name. */ -const getCoreTypeName = ( ioTypeName: string ): string => { +const getCoreTypeName = ( ioTypeName: IOTypeName ): string => { const index = ioTypeName.indexOf( PhetioConstants.IO_TYPE_SUFFIX ); assert && assert( index >= 0, 'IO should be in the type name' ); return ioTypeName.substring( 0, index ); @@ -183,7 +183,7 @@ * Parameterized types should also include a `parameterTypes` field on the IOType. * @param providedOptions */ - public constructor( public readonly typeName: string, providedOptions: IOTypeOptions ) { + public constructor( public readonly typeName: IOTypeName, providedOptions: IOTypeOptions ) { // For reference in the options const supertype = providedOptions.supertype || IOType.ObjectIO; @@ -389,19 +389,18 @@ /** * @param stateObject - the stateObject to validate against - * @param toAssert=false - whether or not to assert when invalid - * @param publicSchemaKeys=[] - * @param privateSchemaKeys=[] + * @param toAssert=false - whether to assert when invalid + * @param schemaKeysPresentInStateObject=[] * @returns if the stateObject is valid or not. */ - public isStateObjectValid( stateObject: StateType, toAssert = false, publicSchemaKeys: string[] = [], privateSchemaKeys: string[] = [] ): boolean { + public isStateObjectValid( stateObject: StateType, toAssert = false, schemaKeysPresentInStateObject: string[] = [] ): boolean { // Set to false when invalid let valid = true; // make sure the stateObject has everything the schema requires and nothing more if ( this.stateSchema ) { - const validSoFar = this.stateSchema.checkStateObjectValid( stateObject, toAssert, publicSchemaKeys, privateSchemaKeys ); + const validSoFar = this.stateSchema.checkStateObjectValid( stateObject, toAssert, schemaKeysPresentInStateObject ); // null as a marker to keep checking up the hierarchy, otherwise we reached our based case because the stateSchema was a value, not a composite if ( validSoFar !== null ) { @@ -410,27 +409,20 @@ } if ( this.supertype ) { - return valid && this.supertype.isStateObjectValid( stateObject, toAssert, publicSchemaKeys, privateSchemaKeys ); + return valid && this.supertype.isStateObjectValid( stateObject, toAssert, schemaKeysPresentInStateObject ); } // When we reach the root, make sure there isn't anything in the stateObject that isn't described by a schema if ( !this.supertype && stateObject && typeof stateObject !== 'string' && !Array.isArray( stateObject ) ) { - const check = ( type: 'public' | 'private', key: string ) => { - const keys = type === 'public' ? publicSchemaKeys : privateSchemaKeys; - const keyValid = keys.includes( key ); + // Visit the state + Object.keys( stateObject ).forEach( key => { + const keyValid = schemaKeysPresentInStateObject.includes( key ); if ( !keyValid ) { valid = false; } - assert && toAssert && assert( keyValid, `stateObject provided a ${type} key that is not in the schema: ${key}` ); - }; - - // Visit the public state - Object.keys( stateObject ).filter( key => key !== '_private' ).forEach( key => check( 'public', key ) ); - - // Visit the private state, if any - // @ts-expect-error stateObjects can take a variety of forms, they don't have to be a record, thus, it is challenging to be graceful to a `_private` key - stateObject._private && Object.keys( stateObject._private ).forEach( key => check( 'private', key ) ); + assert && toAssert && assert( keyValid, `stateObject provided a key that is not in the schema: ${key}` ); + } ); return valid; } @@ -444,7 +436,7 @@ this.isStateObjectValid( stateObject, true ); } - public toString(): string { + public toString(): IOTypeName { return this.typeName; } } @@ -473,7 +465,7 @@ fromStateObject: () => { throw new Error( 'ObjectIO.fromStateObject should not be called' ); }, - stateObjectToCreateElementArguments: stateObject => [], + stateObjectToCreateElementArguments: () => [], applyState: _.noop, metadataDefaults: TandemConstants.PHET_IO_OBJECT_METADATA_DEFAULTS, dataDefaults: { Index: tandem/js/types/StateSchema.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/tandem/js/types/StateSchema.ts b/tandem/js/types/StateSchema.ts --- a/tandem/js/types/StateSchema.ts (revision 9e04ba7e40acc6c7952d9dff599c1201783667bb) +++ b/tandem/js/types/StateSchema.ts (date 1679955010269) @@ -12,25 +12,21 @@ * @author Michael Kauzmann (PhET Interactive Simulations) */ -import validate from '../../../axon/js/validate.js'; import Validation, { Validator } from '../../../axon/js/Validation.js'; import assertMutuallyExclusiveOptions from '../../../phet-core/js/assertMutuallyExclusiveOptions.js'; import optionize from '../../../phet-core/js/optionize.js'; import tandemNamespace from '../tandemNamespace.js'; import IOType from './IOType.js'; import IntentionalAny from '../../../phet-core/js/types/IntentionalAny.js'; +import { IOTypeName } from '../TandemConstants.js'; -export type CompositeSchema = Record & { - _private?: CompositeSchema; -}; +export type CompositeSchema = Record; -type CompositeSchemaAPI = Record & { - _private?: Record; -}; +// As provided in the PhET-iO API json +type CompositeSchemaAPI = Record; -export type CompositeStateObjectType = Record & { - _private?: Record; -}; +// The schema of the stateObject value +export type CompositeStateObjectType = Record; type StateSchemaOptions = { displayString?: string; @@ -38,8 +34,6 @@ compositeSchema?: null | CompositeSchema; }; -type GeneralStateObject = Record; - export default class StateSchema { private readonly displayString: string; private readonly validator: Validator | null; @@ -64,96 +58,56 @@ this.validator = options.validator; this.compositeSchema = options.compositeSchema; - - assert && this.validateStateSchema( this.compositeSchema ); - } - - - /** - * Make sure that a composite state schema is of the correct form. Each value in the object should be an IOType - * Only useful if assert is called. - */ - private validateStateSchema( stateSchema: CompositeSchema | null ): void { - if ( assert && this.isComposite() ) { - - for ( const stateSchemaKey in stateSchema ) { - if ( stateSchema.hasOwnProperty( stateSchemaKey ) ) { - - - if ( stateSchemaKey === '_private' ) { - - // By putting the assignment in this statement, typescript knows the value as a CompositeSchema - const stateSchemaValue = stateSchema[ stateSchemaKey ]; - assert && assert( stateSchemaValue, 'should not be undefined' ); - this.validateStateSchema( stateSchemaValue! ); - } - } - } - } } public defaultApplyState( coreObject: T, stateObject: CompositeStateObjectType ): void { - const applyStateForLevel = ( schema: CompositeSchema, stateObjectLevel: GeneralStateObject ) => { - assert && assert( this.isComposite(), 'defaultApplyState from stateSchema only applies to composite stateSchemas' ); - for ( const stateKey in schema ) { - if ( schema.hasOwnProperty( stateKey ) ) { - if ( stateKey === '_private' ) { - applyStateForLevel( schema._private!, stateObjectLevel._private ); - } - else { + assert && assert( this.isComposite(), 'defaultApplyState from stateSchema only applies to composite stateSchemas' ); + for ( const stateKey in this.compositeSchema ) { + if ( this.compositeSchema.hasOwnProperty( stateKey ) ) { + assert && assert( stateObject.hasOwnProperty( stateKey ), `stateObject does not have expected schema key: ${stateKey}` ); - // The IOType for the key in the composite. - const schemaIOType = schema[ stateKey ]; - assert && assert( stateObjectLevel.hasOwnProperty( stateKey ), `stateObject does not have expected schema key: ${stateKey}` ); + // The IOType for the key in the composite. + const schemaIOType = this.compositeSchema[ stateKey ]; + + const coreObjectAccessor = stateKey.startsWith( '_' ) ? stateKey.substring( 1 ) : stateKey; - // Using fromStateObject to deserialize sub-component - if ( schemaIOType.defaultDeserializationMethod === 'fromStateObject' ) { + // Using fromStateObject to deserialize sub-component + if ( schemaIOType.defaultDeserializationMethod === 'fromStateObject' ) { - // @ts-expect-error, I don't know how to tell typescript that we are accessing an expected key on the PhetioObject subtype. Likely there is no way with making things generic. - coreObject[ stateKey ] = schema[ stateKey ].fromStateObject( stateObjectLevel[ stateKey ] ); - } - else { - assert && assert( schemaIOType.defaultDeserializationMethod === 'applyState', 'unexpected deserialization method' ); + // @ts-expect-error, I don't know how to tell typescript that we are accessing an expected key on the PhetioObject subtype. Likely there is no way with making things generic. + coreObject[ coreObjectAccessor ] = this.compositeSchema[ stateKey ].fromStateObject( stateObject[ coreObjectAccessor ] ); + } + else { + assert && assert( schemaIOType.defaultDeserializationMethod === 'applyState', 'unexpected deserialization method' ); - // Using applyState to deserialize sub-component - // @ts-expect-error, I don't know how to tell typescript that we are accessing an expected key on the PhetioObject subtype. Likely there is no way with making things generic. - schema[ stateKey ].applyState( coreObject[ stateKey ], stateObjectLevel[ stateKey ] ); - } - } - } - } - }; - applyStateForLevel( this.compositeSchema!, stateObject ); + // Using applyState to deserialize sub-component + // @ts-expect-error, I don't know how to tell typescript that we are accessing an expected key on the PhetioObject subtype. Likely there is no way with making things generic. + this.compositeSchema[ stateKey ].applyState( coreObject[ coreObjectAccessor ], stateObject[ coreObjectAccessor ] ); + } + } + } } public defaultToStateObject( coreObject: T ): StateType { assert && assert( this.isComposite(), 'defaultToStateObject from stateSchema only applies to composite stateSchemas' ); - const toStateObjectForSchemaLevel = ( schema: CompositeSchema ) => { + const stateObject = {}; + for ( const stateKey in this.compositeSchema ) { + if ( this.compositeSchema.hasOwnProperty( stateKey ) ) { - const stateObject = {} as StateType; - for ( const stateKey in schema ) { - if ( schema.hasOwnProperty( stateKey ) ) { - if ( stateKey === '_private' ) { - - // @ts-expect-error Still working on _private in https://github.com/phetsims/tandem/issues/282 - stateObject._private = toStateObjectForSchemaLevel( schema._private ); - } - else { + // Trim the '_' if any + const coreObjectAccessor = stateKey.startsWith( '_' ) ? stateKey.substring( 1 ) : stateKey; - // @ts-expect-error I guess we need to support schemas outside of composite here, or tell how to avoid that, https://github.com/phetsims/tandem/issues/261 - assert && assert( coreObject.hasOwnProperty( stateKey ), - `cannot get state because coreObject does not have expected schema key: ${stateKey}` ); + // @ts-expect-error I guess we need to support schemas outside of composite here, or tell how to avoid that, https://github.com/phetsims/tandem/issues/261 + assert && assert( coreObject.hasOwnProperty( coreObjectAccessor ), + `cannot get state because coreObject does not have expected schema key: ${coreObjectAccessor}` ); - // @ts-expect-error https://github.com/phetsims/tandem/issues/261 - stateObject[ stateKey ] = schema[ stateKey ].toStateObject( coreObject[ stateKey ] ); - } - } - } - return stateObject; - }; - return toStateObjectForSchemaLevel( this.compositeSchema! ); + // @ts-expect-error https://github.com/phetsims/tandem/issues/261 + stateObject[ stateKey ] = this.compositeSchema[ stateKey ].toStateObject( coreObject[ coreObjectAccessor ] ); + } + } + return stateObject as StateType; } /** @@ -163,50 +117,43 @@ return !!this.compositeSchema; } - /** * Check if a given stateObject is as valid as can be determined by this StateSchema. Will return null if valid, but * needs more checking up and down the hierarchy. * * @param stateObject - the stateObject to validate against * @param toAssert - whether to assert when invalid - * @param publicSchemaKeys - to be populated with any public keys this StateSchema is responsible for - * @param privateSchemaKeys - to be populated with any private keys this StateSchema is responsible for + * @param schemaKeysPresentInStateObject - to be populated with any keys this StateSchema is responsible for. * @returns boolean if validity can be checked, null if valid, but next in the hierarchy is needed */ - public checkStateObjectValid( stateObject: StateType, toAssert: boolean, publicSchemaKeys: string[], privateSchemaKeys: string[] ): boolean | null { + public checkStateObjectValid( stateObject: StateType, toAssert: boolean, schemaKeysPresentInStateObject: string[] ): boolean | null { if ( this.isComposite() ) { const compositeStateObject = stateObject as CompositeStateObjectType; const schema = this.compositeSchema!; let valid = null; - const checkLevel = ( schemaLevel: CompositeSchema, objectLevel: GeneralStateObject, keyList: string[], exclude: string | null ) => { - if ( !objectLevel ) { - valid = false; - assert && toAssert && assert( false, 'There was no stateObject, but there was a state schema saying there should be', schemaLevel ); - return; - } - Object.keys( schemaLevel ).filter( ( k: string ) => k !== exclude ).forEach( key => { - const validKey = objectLevel.hasOwnProperty( key ); - if ( !validKey ) { - valid = false; - } - assert && toAssert && assert( validKey, `${key} in state schema but not in the state object` ); - schemaLevel[ key ].validateStateObject( objectLevel[ key ] ); - keyList.push( key ); - } ); - }; - - checkLevel( schema, compositeStateObject, publicSchemaKeys, '_private' ); - schema._private && checkLevel( schema._private, compositeStateObject._private!, privateSchemaKeys, null ); + if ( !compositeStateObject ) { + valid = false; + assert && toAssert && assert( false, 'There was no stateObject, but there was a state schema saying there should be', schema ); + return valid; + } + Object.keys( schema ).forEach( key => { + if ( !compositeStateObject.hasOwnProperty( key ) ) { + valid = false; + assert && toAssert && assert( false, `${key} in state schema but not in the state object` ); + } + else { + // TODO: this should call isStateObjectValid and mutate `valid` instead of firing assertions as validation. TODO: But how to have a good message + schema[ key ].validateStateObject( compositeStateObject[ key ] ); + } + schemaKeysPresentInStateObject.push( key ); + } ); return valid; } else { assert && assert( this.validator, 'validator must be present if not composite' ); const valueStateObject = stateObject; - if ( toAssert ) { - validate( valueStateObject, this.validator! ); - } + assert && toAssert && assert( Validation.getValidationError( valueStateObject, this.validator! ) === null ); return Validation.isValueValid( valueStateObject, this.validator! ); } @@ -218,18 +165,10 @@ public getRelatedTypes(): IOType[] { const relatedTypes: IOType[] = []; - // to support IOTypes from public and private state - const getRelatedStateTypeForLevel = ( stateSchema: CompositeSchema ) => { - Object.keys( stateSchema ).forEach( stateSchemaKey => { - - // Support keywords in state like "private" - stateSchema[ stateSchemaKey ] instanceof IOType && relatedTypes.push( stateSchema[ stateSchemaKey ] ); - } ); - }; - if ( this.compositeSchema ) { - getRelatedStateTypeForLevel( this.compositeSchema ); - this.compositeSchema._private && getRelatedStateTypeForLevel( this.compositeSchema._private ); + Object.keys( this.compositeSchema ).forEach( stateSchemaKey => { + this.compositeSchema![ stateSchemaKey ] instanceof IOType && relatedTypes.push( this.compositeSchema![ stateSchemaKey ] ); + } ); } return relatedTypes; } @@ -241,11 +180,7 @@ */ public getStateSchemaAPI(): string | CompositeSchemaAPI { if ( this.isComposite() ) { - const stateSchemaAPI = _.mapValues( this.compositeSchema, value => value.typeName ) as CompositeSchemaAPI; - if ( this.compositeSchema!._private ) { - stateSchemaAPI._private = _.mapValues( this.compositeSchema!._private, value => value.typeName ); - } - return stateSchemaAPI; + return _.mapValues( this.compositeSchema, value => value.typeName )!; } else { return this.displayString; Index: proportion-playground/js/common/model/paint/PaintChoice.js IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/proportion-playground/js/common/model/paint/PaintChoice.js b/proportion-playground/js/common/model/paint/PaintChoice.js --- a/proportion-playground/js/common/model/paint/PaintChoice.js (revision 03b9b292f1918da93149b92fe7ad0ff0e64fecf3) +++ b/proportion-playground/js/common/model/paint/PaintChoice.js (date 1679951475584) @@ -135,18 +135,14 @@ documentation: 'A combination of a left and right effective color', toStateObject( paintChoice ) { return { - _private: { - choice: ( paintChoice === PaintChoice.BLUE_YELLOW ) ? 'BLUE_YELLOW' : ( paintChoice === PaintChoice.RED_YELLOW ? 'RED_YELLOW' : 'BLACK_WHITE' ) - } + _choice: ( paintChoice === PaintChoice.BLUE_YELLOW ) ? 'BLUE_YELLOW' : ( paintChoice === PaintChoice.RED_YELLOW ? 'RED_YELLOW' : 'BLACK_WHITE' ) }; }, stateSchema: { - _private: { - choice: StringIO - } + _choice: StringIO }, fromStateObject( stateObject ) { - return PaintChoice[ stateObject._private.choice ]; + return PaintChoice[ stateObject._choice ]; } } ); PaintChoice.PaintChoiceIO = PaintChoiceIO; Index: states-of-matter/js/common/model/MultipleParticleModel.js IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/states-of-matter/js/common/model/MultipleParticleModel.js b/states-of-matter/js/common/model/MultipleParticleModel.js --- a/states-of-matter/js/common/model/MultipleParticleModel.js (revision da663e80b553643694da2d4038806a1c99e29d3b) +++ b/states-of-matter/js/common/model/MultipleParticleModel.js (date 1679951475594) @@ -1366,18 +1366,16 @@ */ toStateObject() { return { - _private: { // to indicate that it is needed for state, but shouldn't be shown in Studio - substance: EnumerationIO( SubstanceType ).toStateObject( this.substanceProperty.value ), - isExploded: this.isExplodedProperty.value, - containerHeight: this.containerHeightProperty.value, - gravitationalAcceleration: this.gravitationalAcceleration, - normalizedLidVelocityY: this.normalizedLidVelocityY, - heatingCoolingAmount: this.heatingCoolingAmountProperty.value, - moleculeDataSet: MoleculeForceAndMotionDataSet.MoleculeForceAndMotionDataSetIO.toStateObject( this.moleculeDataSet ), - isoKineticThermostatState: this.isoKineticThermostat.toStateObject(), - andersenThermostatState: this.andersenThermostat.toStateObject(), - moleculeForcesAndMotionCalculatorPressure: this.moleculeForceAndMotionCalculator.pressureProperty.value - } + _substance: EnumerationIO( SubstanceType ).toStateObject( this.substanceProperty.value ), + _isExploded: this.isExplodedProperty.value, + _containerHeight: this.containerHeightProperty.value, + _gravitationalAcceleration: this.gravitationalAcceleration, + _normalizedLidVelocityY: this.normalizedLidVelocityY, + _heatingCoolingAmount: this.heatingCoolingAmountProperty.value, + _moleculeDataSet: MoleculeForceAndMotionDataSet.MoleculeForceAndMotionDataSetIO.toStateObject( this.moleculeDataSet ), + _isoKineticThermostatState: this.isoKineticThermostat.toStateObject(), + _andersenThermostatState: this.andersenThermostat.toStateObject(), + _moleculeForcesAndMotionCalculatorPressure: this.moleculeForceAndMotionCalculator.pressureProperty.value }; } @@ -1392,22 +1390,22 @@ // Setting the substance initializes a bunch of model parameters, so this is done first, then other items that may // have been affected are set. - this.substanceProperty.set( EnumerationIO( SubstanceType ).fromStateObject( stateObject._private.substance ) ); + this.substanceProperty.set( EnumerationIO( SubstanceType ).fromStateObject( stateObject._substance ) ); // Set properties that may have been updated by setting the substance. - this.isExplodedProperty.set( stateObject._private.isExploded ); - this.containerHeightProperty.set( stateObject._private.containerHeight ); - this.heatingCoolingAmountProperty.set( stateObject._private.heatingCoolingAmount ); - this.gravitationalAcceleration = stateObject._private.gravitationalAcceleration; - this.normalizedLidVelocityY = stateObject._private.normalizedLidVelocityY; - this.isoKineticThermostat.setState( stateObject._private.isoKineticThermostatState ); - this.andersenThermostat.setState( stateObject._private.andersenThermostatState ); + this.isExplodedProperty.set( stateObject._isExploded ); + this.containerHeightProperty.set( stateObject._containerHeight ); + this.heatingCoolingAmountProperty.set( stateObject._heatingCoolingAmount ); + this.gravitationalAcceleration = stateObject._gravitationalAcceleration; + this.normalizedLidVelocityY = stateObject._normalizedLidVelocityY; + this.isoKineticThermostat.setState( stateObject._isoKineticThermostatState ); + this.andersenThermostat.setState( stateObject._andersenThermostatState ); // Set the molecule data set. This includes all the positions, velocities, etc. for the particles. - this.moleculeDataSet.setState( stateObject._private.moleculeDataSet ); + this.moleculeDataSet.setState( stateObject._moleculeDataSet ); // Preset the pressure in the accumulator that tracks it so that it doesn't have to start from zero. - this.moleculeForceAndMotionCalculator.presetPressure( stateObject._private.moleculeForcesAndMotionCalculatorPressure ); + this.moleculeForceAndMotionCalculator.presetPressure( stateObject._moleculeForcesAndMotionCalculatorPressure ); // Make sure that we have the right number of scaled (i.e. non-normalized) atoms. const numberOfNormalizedMolecules = this.moleculeDataSet.numberOfMolecules; @@ -1441,18 +1439,16 @@ toStateObject: multipleParticleModel => multipleParticleModel.toStateObject(), applyState: ( multipleParticleModel, state ) => multipleParticleModel.applyState( state ), stateSchema: { - _private: { - substance: EnumerationIO( SubstanceType ), - isExploded: BooleanIO, - containerHeight: NumberIO, - gravitationalAcceleration: NumberIO, - normalizedLidVelocityY: NumberIO, - heatingCoolingAmount: NumberIO, - moleculeDataSet: MoleculeForceAndMotionDataSet.MoleculeForceAndMotionDataSetIO, - isoKineticThermostatState: IsokineticThermostat.IsoKineticThermostatIO, - andersenThermostatState: AndersenThermostat.AndersenThermostatIO, - moleculeForcesAndMotionCalculatorPressure: NumberIO - } + _substance: EnumerationIO( SubstanceType ), + _isExploded: BooleanIO, + _containerHeight: NumberIO, + _gravitationalAcceleration: NumberIO, + _normalizedLidVelocityY: NumberIO, + _heatingCoolingAmount: NumberIO, + _moleculeDataSet: MoleculeForceAndMotionDataSet.MoleculeForceAndMotionDataSetIO, + _isoKineticThermostatState: IsokineticThermostat.IsoKineticThermostatIO, + _andersenThermostatState: AndersenThermostat.AndersenThermostatIO, + _moleculeForcesAndMotionCalculatorPressure: NumberIO } } ); Index: tandem/js/TandemConstants.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/tandem/js/TandemConstants.ts b/tandem/js/TandemConstants.ts --- a/tandem/js/TandemConstants.ts (revision 9e04ba7e40acc6c7952d9dff599c1201783667bb) +++ b/tandem/js/TandemConstants.ts (date 1679953342205) @@ -76,6 +76,8 @@ // Like the old API schema, where keys are the full, dot-separated phetioID export type APIFlat = Record; +export type IOTypeName = string; + export type PhetioObjectMetadata = { // Used in PhetioObjectOptions @@ -90,7 +92,7 @@ phetioDesigned: boolean; // Specific to Metadata - phetioTypeName: string; + phetioTypeName: IOTypeName; phetioIsArchetype: boolean; phetioArchetypePhetioID?: string | null;
samreid commented 1 year ago

I messaged @pixelzoom and @jessegreenberg and our slack developer channel:

@zepumph and I really want to push major improvements and bugfixes for https://github.com/phetsims/tandem/issues/282 but we don’t want to sneak that in right before RCs are created. Please keep us posted when the RCs are all made.

samreid commented 1 year ago

@pixelzoom reported CG 1.0 branch was created, so this is no longer on hold.

samreid commented 1 year ago

I addressed a remaining TODO in the code, and regenerated API files. I was surprised to see a change in the calculus grapher API. This was a more risky change, and I'll message slack about it, and close.

zepumph commented 1 year ago

Seems like CT problems, see https://github.com/phetsims/wave-on-a-string/issues/155

samreid commented 1 year ago

https://github.com/phetsims/wave-on-a-string/issues/155 addressed separately. I skimmed the latest CT column and saw problems like Uncaught Error: Assertion failed: Disposable.dispose() call is missing from an overridden dispose method which I mentioned in the slack CT channel. Closing.