diff --git a/packages/dds/tree/api-report/tree.alpha.api.md b/packages/dds/tree/api-report/tree.alpha.api.md index d36cd4c86252..87a2c03f079f 100644 --- a/packages/dds/tree/api-report/tree.alpha.api.md +++ b/packages/dds/tree/api-report/tree.alpha.api.md @@ -926,9 +926,9 @@ export class SchemaFactoryAlpha extends SchemaFactory { - object, const TCustomMetadata = unknown>(name: Name, fields: T, options?: ObjectSchemaOptions): TreeNodeSchemaClass, NodeKind.Object, TreeObjectNode>, object & InsertableObjectFromSchemaRecord, true, T>; + object, const TCustomMetadata = unknown>(name: Name, fields: T, options?: ObjectSchemaOptions): TreeNodeSchemaClass, NodeKind.Object, TreeObjectNode>, object & InsertableObjectFromSchemaRecord, true, T, never, TCustomMetadata>; // (undocumented) - objectRecursive, const TCustomMetadata = unknown>(name: Name, t: T, options?: ObjectSchemaOptions): TreeNodeSchemaClass, NodeKind.Object, System_Unsafe.TreeObjectNodeUnsafe>, object & System_Unsafe.InsertableObjectFromSchemaRecordUnsafe, false, T>; + objectRecursive, const TCustomMetadata = unknown>(name: Name, t: T, options?: ObjectSchemaOptions): TreeNodeSchemaClass, NodeKind.Object, System_Unsafe.TreeObjectNodeUnsafe>, object & System_Unsafe.InsertableObjectFromSchemaRecordUnsafe, false, T, never, TCustomMetadata>; record(allowedTypes: T): TreeNodeSchemaNonClass`>, NodeKind.Record, TreeRecordNode & WithType`>, NodeKind.Record>, RecordNodeInsertableData, true, T, undefined>; record(name: Name, allowedTypes: T, options?: NodeSchemaOptions): TreeNodeSchemaClass, NodeKind.Record, TreeRecordNode & WithType, NodeKind.Record>, RecordNodeInsertableData, true, T, undefined, TCustomMetadata>; recordRecursive(name: Name, allowedTypes: T, options?: NodeSchemaOptions): TreeNodeSchemaClass, NodeKind.Record, TreeRecordNodeUnsafe & WithType, NodeKind.Record, unknown>, { diff --git a/packages/dds/tree/api-report/tree.beta.api.md b/packages/dds/tree/api-report/tree.beta.api.md index 8ca706c1baca..7b6be89ed6b5 100644 --- a/packages/dds/tree/api-report/tree.beta.api.md +++ b/packages/dds/tree/api-report/tree.beta.api.md @@ -483,9 +483,9 @@ export const SchemaFactory_base: SchemaStatics & (new () => SchemaStatics); // @beta export class SchemaFactoryBeta extends SchemaFactory { - object, const TCustomMetadata = unknown>(name: Name, fields: T, options?: ObjectSchemaOptions): TreeNodeSchemaClass, NodeKind.Object, TreeObjectNode>, object & InsertableObjectFromSchemaRecord, true, T>; + object, const TCustomMetadata = unknown>(name: Name, fields: T, options?: ObjectSchemaOptions): TreeNodeSchemaClass, NodeKind.Object, TreeObjectNode>, object & InsertableObjectFromSchemaRecord, true, T, never, TCustomMetadata>; // (undocumented) - objectRecursive, const TCustomMetadata = unknown>(name: Name, t: T, options?: ObjectSchemaOptions): TreeNodeSchemaClass, NodeKind.Object, System_Unsafe.TreeObjectNodeUnsafe>, object & System_Unsafe.InsertableObjectFromSchemaRecordUnsafe, false, T>; + objectRecursive, const TCustomMetadata = unknown>(name: Name, t: T, options?: ObjectSchemaOptions): TreeNodeSchemaClass, NodeKind.Object, System_Unsafe.TreeObjectNodeUnsafe>, object & System_Unsafe.InsertableObjectFromSchemaRecordUnsafe, false, T, never, TCustomMetadata>; record(allowedTypes: T): TreeNodeSchemaNonClass`>, NodeKind.Record, TreeRecordNode & WithType`>, NodeKind.Record>, RecordNodeInsertableData, true, T, undefined>; record(name: Name, allowedTypes: T, options?: NodeSchemaOptions): TreeNodeSchemaClass, NodeKind.Record, TreeRecordNode & WithType, NodeKind.Record>, RecordNodeInsertableData, true, T, undefined, TCustomMetadata>; recordRecursive(name: Name, allowedTypes: T, options?: NodeSchemaOptions): TreeNodeSchemaClass, NodeKind.Record, TreeRecordNodeUnsafe & WithType, NodeKind.Record, unknown>, { diff --git a/packages/dds/tree/api-report/tree.legacy.beta.api.md b/packages/dds/tree/api-report/tree.legacy.beta.api.md index dda55858c438..f2e0a99f8d0f 100644 --- a/packages/dds/tree/api-report/tree.legacy.beta.api.md +++ b/packages/dds/tree/api-report/tree.legacy.beta.api.md @@ -486,9 +486,9 @@ export const SchemaFactory_base: SchemaStatics & (new () => SchemaStatics); // @beta export class SchemaFactoryBeta extends SchemaFactory { - object, const TCustomMetadata = unknown>(name: Name, fields: T, options?: ObjectSchemaOptions): TreeNodeSchemaClass, NodeKind.Object, TreeObjectNode>, object & InsertableObjectFromSchemaRecord, true, T>; + object, const TCustomMetadata = unknown>(name: Name, fields: T, options?: ObjectSchemaOptions): TreeNodeSchemaClass, NodeKind.Object, TreeObjectNode>, object & InsertableObjectFromSchemaRecord, true, T, never, TCustomMetadata>; // (undocumented) - objectRecursive, const TCustomMetadata = unknown>(name: Name, t: T, options?: ObjectSchemaOptions): TreeNodeSchemaClass, NodeKind.Object, System_Unsafe.TreeObjectNodeUnsafe>, object & System_Unsafe.InsertableObjectFromSchemaRecordUnsafe, false, T>; + objectRecursive, const TCustomMetadata = unknown>(name: Name, t: T, options?: ObjectSchemaOptions): TreeNodeSchemaClass, NodeKind.Object, System_Unsafe.TreeObjectNodeUnsafe>, object & System_Unsafe.InsertableObjectFromSchemaRecordUnsafe, false, T, never, TCustomMetadata>; record(allowedTypes: T): TreeNodeSchemaNonClass`>, NodeKind.Record, TreeRecordNode & WithType`>, NodeKind.Record>, RecordNodeInsertableData, true, T, undefined>; record(name: Name, allowedTypes: T, options?: NodeSchemaOptions): TreeNodeSchemaClass, NodeKind.Record, TreeRecordNode & WithType, NodeKind.Record>, RecordNodeInsertableData, true, T, undefined, TCustomMetadata>; recordRecursive(name: Name, allowedTypes: T, options?: NodeSchemaOptions): TreeNodeSchemaClass, NodeKind.Record, TreeRecordNodeUnsafe & WithType, NodeKind.Record, unknown>, { diff --git a/packages/dds/tree/src/simple-tree/api/schemaFactoryBeta.ts b/packages/dds/tree/src/simple-tree/api/schemaFactoryBeta.ts index 745a435f47a4..c0eb5c786d37 100644 --- a/packages/dds/tree/src/simple-tree/api/schemaFactoryBeta.ts +++ b/packages/dds/tree/src/simple-tree/api/schemaFactoryBeta.ts @@ -88,7 +88,9 @@ export class SchemaFactoryBeta< TreeObjectNode>, object & InsertableObjectFromSchemaRecord, true, - T + T, + never, + TCustomMetadata > { return objectSchema(scoped(this, name), fields, true, { ...defaultSchemaFactoryObjectOptions, @@ -110,7 +112,9 @@ export class SchemaFactoryBeta< System_Unsafe.TreeObjectNodeUnsafe>, object & System_Unsafe.InsertableObjectFromSchemaRecordUnsafe, false, - T + T, + never, + TCustomMetadata > { type TScopedName = ScopedSchemaName; return this.object( diff --git a/packages/dds/tree/src/test/simple-tree/api/schemaFactory.spec.ts b/packages/dds/tree/src/test/simple-tree/api/schemaFactory.spec.ts index 3210770ae536..a15bd0850a55 100644 --- a/packages/dds/tree/src/test/simple-tree/api/schemaFactory.spec.ts +++ b/packages/dds/tree/src/test/simple-tree/api/schemaFactory.spec.ts @@ -105,6 +105,42 @@ import { EmptyKey } from "../../../core/index.js"; type FromArray = TreeNodeFromImplicitAllowedTypes<[typeof Note, typeof Note]>; type _check5 = requireTrue>; } + // TreeNodeFromImplicitAllowedTypes with a class + { + class NoteCustomized extends schema.object("Note", { text: schema.string }) { + public test: boolean = false; + } + + type _check = requireAssignableTo; + type _checkNodeType = requireAssignableTo< + typeof NoteCustomized, + TreeNodeSchema + >; + + type Instance = InstanceType; + type _checkInstance = requireTrue>; + + type Test = TreeNodeFromImplicitAllowedTypes; + type _check2 = requireTrue>; + + type _check3 = requireTrue< + areSafelyAssignable< + TreeNodeFromImplicitAllowedTypes<[typeof NoteCustomized]>, + NoteCustomized + > + >; + type _check4 = requireTrue< + areSafelyAssignable< + TreeNodeFromImplicitAllowedTypes<[() => typeof NoteCustomized]>, + NoteCustomized + > + >; + + type FromArray = TreeNodeFromImplicitAllowedTypes< + [typeof NoteCustomized, typeof NoteCustomized] + >; + type _check5 = requireTrue>; + } // TreeFieldFromImplicitField { @@ -383,7 +419,7 @@ describe("schemaFactory", () => { ); }); - it("Node schema metadata", () => { + it("Node schema metadata - beta", () => { const factory = new SchemaFactoryBeta(""); const fooMetadata = { @@ -391,7 +427,7 @@ describe("schemaFactory", () => { custom: { baz: true, }, - }; + } as const; class Foo extends factory.object( "Foo", @@ -399,13 +435,62 @@ describe("schemaFactory", () => { { metadata: fooMetadata }, ) {} + // Ensure `Foo.metadata` is typed as we expect, and we can access its fields without casting. + const description = Foo.metadata.description; + type _check1 = requireTrue>; + + const custom = Foo.metadata.custom; + + // Currently it is impossible to have required custom metadata: it always includes undefined as an option. + // TODO: having a way to make metadata required would be nice. + + type _check2 = requireTrue< + areSafelyAssignable + >; + assert(custom !== undefined); + + const baz = custom.baz; + type _check3 = requireTrue>; + + // This must be checked after the types are checked to avoid it narrowing and making the type checks above not test anything. assert.deepEqual(Foo.metadata, fooMetadata); + }); + + it("Node schema metadata - alpha", () => { + const factory = new SchemaFactoryAlpha(""); + + const fooMetadata = { + description: "An object called Foo", + custom: { + baz: true, + }, + } as const; + + class Foo extends factory.objectAlpha( + "Foo", + { bar: factory.number }, + { metadata: fooMetadata }, + ) {} // Ensure `Foo.metadata` is typed as we expect, and we can access its fields without casting. const description = Foo.metadata.description; - const baz = Foo.metadata.custom.baz; - type _check1 = requireTrue>; - type _check2 = requireTrue>; + type _check1 = requireTrue>; + + const custom = Foo.metadata.custom; + + // Currently it is impossible to have required custom metadata: it always includes undefined as an option. + // TODO: having a way to make metadata required would be nice. + + type _check2 = requireTrue< + areSafelyAssignable + >; + assert(custom !== undefined); + + const baz = custom.baz; + type _check3 = requireTrue>; + + // This must be checked after the types are checked to avoid it narrowing and making the type checks above not test anything. + assert.deepEqual(Foo.metadata, fooMetadata); }); it("Field schema metadata", () => { diff --git a/packages/dds/tree/src/test/simple-tree/api/schemaFactoryRecursive.spec.ts b/packages/dds/tree/src/test/simple-tree/api/schemaFactoryRecursive.spec.ts index 07609b93dff9..c9054cb5028c 100644 --- a/packages/dds/tree/src/test/simple-tree/api/schemaFactoryRecursive.spec.ts +++ b/packages/dds/tree/src/test/simple-tree/api/schemaFactoryRecursive.spec.ts @@ -28,6 +28,7 @@ import { type AllowedTypesFull, type ImplicitAllowedTypes, type AllowedTypes, + SchemaFactoryBeta, } from "../../../simple-tree/index.js"; import { allowUnused, @@ -498,8 +499,8 @@ describe("SchemaFactory Recursive methods", () => { } }); - it("Node schema metadata", () => { - const factory = new SchemaFactoryAlpha(""); + it("Node schema metadata - beta", () => { + const factory = new SchemaFactoryBeta(""); class Foo extends factory.objectRecursive( "Foo", { bar: [() => Foo] }, @@ -511,14 +512,59 @@ describe("SchemaFactory Recursive methods", () => { }, ) {} + const custom = Foo.metadata.custom; + + // Currently it is impossible to have required custom metadata: it always includes undefined as an option. + // TODO: having a way to make metadata required would be nice. + type _check1 = requireTrue< + areSafelyAssignable + >; + assert(custom !== undefined); + + // Ensure `Foo.metadata` is typed as we expect, and we can access its fields without casting. + const baz = custom.baz; + + type _check2 = requireTrue>; + + // This must be checked after the types are checked to avoid it narrowing and making the type checks above not test anything. assert.deepEqual(Foo.metadata, { description: "A recursive object called Foo", custom: { baz: true }, }); + }); + + it("Node schema metadata - alpha", () => { + const factory = new SchemaFactoryAlpha(""); + class Foo extends factory.objectRecursive( + "Foo", + { bar: [() => Foo] }, + { + metadata: { + description: "A recursive object called Foo", + custom: { baz: true }, + }, + }, + ) {} + + const custom = Foo.metadata.custom; + + // Currently it is impossible to have required custom metadata: it always includes undefined as an option. + // TODO: having a way to make metadata required would be nice. + type _check1 = requireTrue< + areSafelyAssignable + >; + assert(custom !== undefined); // Ensure `Foo.metadata` is typed as we expect, and we can access its fields without casting. - const baz = Foo.metadata.custom.baz; - type _check1 = requireTrue>; + const baz = custom.baz; + + type _check2 = requireTrue>; + + // This must be checked after the types are checked to avoid it narrowing and making the type checks above not test anything. + assert.deepEqual(Foo.metadata, { + description: "A recursive object called Foo", + custom: { baz: true }, + }); }); }); describe("ValidateRecursiveSchema", () => { diff --git a/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md b/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md index f98a5ca6010d..1dd71e52faea 100644 --- a/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md +++ b/packages/framework/fluid-framework/api-report/fluid-framework.alpha.api.md @@ -1296,9 +1296,9 @@ export class SchemaFactoryAlpha extends SchemaFactory { - object, const TCustomMetadata = unknown>(name: Name, fields: T, options?: ObjectSchemaOptions): TreeNodeSchemaClass, NodeKind.Object, TreeObjectNode>, object & InsertableObjectFromSchemaRecord, true, T>; + object, const TCustomMetadata = unknown>(name: Name, fields: T, options?: ObjectSchemaOptions): TreeNodeSchemaClass, NodeKind.Object, TreeObjectNode>, object & InsertableObjectFromSchemaRecord, true, T, never, TCustomMetadata>; // (undocumented) - objectRecursive, const TCustomMetadata = unknown>(name: Name, t: T, options?: ObjectSchemaOptions): TreeNodeSchemaClass, NodeKind.Object, System_Unsafe.TreeObjectNodeUnsafe>, object & System_Unsafe.InsertableObjectFromSchemaRecordUnsafe, false, T>; + objectRecursive, const TCustomMetadata = unknown>(name: Name, t: T, options?: ObjectSchemaOptions): TreeNodeSchemaClass, NodeKind.Object, System_Unsafe.TreeObjectNodeUnsafe>, object & System_Unsafe.InsertableObjectFromSchemaRecordUnsafe, false, T, never, TCustomMetadata>; record(allowedTypes: T): TreeNodeSchemaNonClass`>, NodeKind.Record, TreeRecordNode & WithType`>, NodeKind.Record>, RecordNodeInsertableData, true, T, undefined>; record(name: Name, allowedTypes: T, options?: NodeSchemaOptions): TreeNodeSchemaClass, NodeKind.Record, TreeRecordNode & WithType, NodeKind.Record>, RecordNodeInsertableData, true, T, undefined, TCustomMetadata>; recordRecursive(name: Name, allowedTypes: T, options?: NodeSchemaOptions): TreeNodeSchemaClass, NodeKind.Record, TreeRecordNodeUnsafe & WithType, NodeKind.Record, unknown>, { diff --git a/packages/framework/fluid-framework/api-report/fluid-framework.beta.api.md b/packages/framework/fluid-framework/api-report/fluid-framework.beta.api.md index 72546ecd054d..ca4a4ce5bf0a 100644 --- a/packages/framework/fluid-framework/api-report/fluid-framework.beta.api.md +++ b/packages/framework/fluid-framework/api-report/fluid-framework.beta.api.md @@ -847,9 +847,9 @@ export const SchemaFactory_base: SchemaStatics & (new () => SchemaStatics); // @beta export class SchemaFactoryBeta extends SchemaFactory { - object, const TCustomMetadata = unknown>(name: Name, fields: T, options?: ObjectSchemaOptions): TreeNodeSchemaClass, NodeKind.Object, TreeObjectNode>, object & InsertableObjectFromSchemaRecord, true, T>; + object, const TCustomMetadata = unknown>(name: Name, fields: T, options?: ObjectSchemaOptions): TreeNodeSchemaClass, NodeKind.Object, TreeObjectNode>, object & InsertableObjectFromSchemaRecord, true, T, never, TCustomMetadata>; // (undocumented) - objectRecursive, const TCustomMetadata = unknown>(name: Name, t: T, options?: ObjectSchemaOptions): TreeNodeSchemaClass, NodeKind.Object, System_Unsafe.TreeObjectNodeUnsafe>, object & System_Unsafe.InsertableObjectFromSchemaRecordUnsafe, false, T>; + objectRecursive, const TCustomMetadata = unknown>(name: Name, t: T, options?: ObjectSchemaOptions): TreeNodeSchemaClass, NodeKind.Object, System_Unsafe.TreeObjectNodeUnsafe>, object & System_Unsafe.InsertableObjectFromSchemaRecordUnsafe, false, T, never, TCustomMetadata>; record(allowedTypes: T): TreeNodeSchemaNonClass`>, NodeKind.Record, TreeRecordNode & WithType`>, NodeKind.Record>, RecordNodeInsertableData, true, T, undefined>; record(name: Name, allowedTypes: T, options?: NodeSchemaOptions): TreeNodeSchemaClass, NodeKind.Record, TreeRecordNode & WithType, NodeKind.Record>, RecordNodeInsertableData, true, T, undefined, TCustomMetadata>; recordRecursive(name: Name, allowedTypes: T, options?: NodeSchemaOptions): TreeNodeSchemaClass, NodeKind.Record, TreeRecordNodeUnsafe & WithType, NodeKind.Record, unknown>, { diff --git a/packages/framework/fluid-framework/api-report/fluid-framework.legacy.beta.api.md b/packages/framework/fluid-framework/api-report/fluid-framework.legacy.beta.api.md index 1fbc960c7b2b..26daec88773f 100644 --- a/packages/framework/fluid-framework/api-report/fluid-framework.legacy.beta.api.md +++ b/packages/framework/fluid-framework/api-report/fluid-framework.legacy.beta.api.md @@ -1125,9 +1125,9 @@ export const SchemaFactory_base: SchemaStatics & (new () => SchemaStatics); // @beta export class SchemaFactoryBeta extends SchemaFactory { - object, const TCustomMetadata = unknown>(name: Name, fields: T, options?: ObjectSchemaOptions): TreeNodeSchemaClass, NodeKind.Object, TreeObjectNode>, object & InsertableObjectFromSchemaRecord, true, T>; + object, const TCustomMetadata = unknown>(name: Name, fields: T, options?: ObjectSchemaOptions): TreeNodeSchemaClass, NodeKind.Object, TreeObjectNode>, object & InsertableObjectFromSchemaRecord, true, T, never, TCustomMetadata>; // (undocumented) - objectRecursive, const TCustomMetadata = unknown>(name: Name, t: T, options?: ObjectSchemaOptions): TreeNodeSchemaClass, NodeKind.Object, System_Unsafe.TreeObjectNodeUnsafe>, object & System_Unsafe.InsertableObjectFromSchemaRecordUnsafe, false, T>; + objectRecursive, const TCustomMetadata = unknown>(name: Name, t: T, options?: ObjectSchemaOptions): TreeNodeSchemaClass, NodeKind.Object, System_Unsafe.TreeObjectNodeUnsafe>, object & System_Unsafe.InsertableObjectFromSchemaRecordUnsafe, false, T, never, TCustomMetadata>; record(allowedTypes: T): TreeNodeSchemaNonClass`>, NodeKind.Record, TreeRecordNode & WithType`>, NodeKind.Record>, RecordNodeInsertableData, true, T, undefined>; record(name: Name, allowedTypes: T, options?: NodeSchemaOptions): TreeNodeSchemaClass, NodeKind.Record, TreeRecordNode & WithType, NodeKind.Record>, RecordNodeInsertableData, true, T, undefined, TCustomMetadata>; recordRecursive(name: Name, allowedTypes: T, options?: NodeSchemaOptions): TreeNodeSchemaClass, NodeKind.Record, TreeRecordNodeUnsafe & WithType, NodeKind.Record, unknown>, {