From 45d863cd1bdeac086861e140ff7608af311e4cfd Mon Sep 17 00:00:00 2001 From: "Craig Macomber (Microsoft)" <42876482+CraigMacomber@users.noreply.github.com> Date: Thu, 15 Aug 2024 14:36:39 -0700 Subject: [PATCH 1/5] Add schema symbol to TreeNodes to allow NodeKind based narrowing. --- .changeset/fruity-birds-love.md | 34 +++++++++ .../dds/tree/api-report/tree.alpha.api.md | 31 +++++--- packages/dds/tree/api-report/tree.beta.api.md | 31 +++++--- .../dds/tree/api-report/tree.public.api.md | 31 +++++--- packages/dds/tree/src/index.ts | 7 +- .../tree/src/simple-tree/api/schemaFactory.ts | 16 ++-- .../simple-tree/api/schemaFactoryRecursive.ts | 2 +- .../dds/tree/src/simple-tree/arrayNode.ts | 27 ++++--- .../dds/tree/src/simple-tree/core/index.ts | 2 +- .../dds/tree/src/simple-tree/core/types.ts | 14 +++- .../dds/tree/src/simple-tree/core/withType.ts | 33 ++++++++- packages/dds/tree/src/simple-tree/index.ts | 1 + packages/dds/tree/src/simple-tree/mapNode.ts | 8 +- .../dds/tree/src/simple-tree/objectNode.ts | 12 ++- .../dds/tree/src/simple-tree/typesUnsafe.ts | 2 +- .../simple-tree/api/schemaFactory.spec.ts | 74 +++++++++++++++++++ .../src/test/simple-tree/core/types.spec.ts | 16 ++++ .../api-report/fluid-framework.beta.api.md | 31 +++++--- .../fluid-framework.legacy.alpha.api.md | 31 +++++--- .../fluid-framework.legacy.public.api.md | 31 +++++--- .../api-report/fluid-framework.public.api.md | 31 +++++--- 21 files changed, 350 insertions(+), 115 deletions(-) create mode 100644 .changeset/fruity-birds-love.md diff --git a/.changeset/fruity-birds-love.md b/.changeset/fruity-birds-love.md new file mode 100644 index 000000000000..68c31cb74ade --- /dev/null +++ b/.changeset/fruity-birds-love.md @@ -0,0 +1,34 @@ +--- +"fluid-framework": minor +"@fluidframework/tree": minor +--- +--- +"section": "tree" +--- +Enable compile time type narrowing based on a TreeNode's NodeKind. + +TreeNode's schema aware APIs implement WithType, which now has a NodeKind parameter that can be used to narrow TreeNodes based on NodeKind. + +Example: + +```typescript +function getKeys(node: TreeNode & WithType): number[]; +function getKeys(node: TreeNode & WithType): string[]; +function getKeys(node: TreeNode): string[] | number[]; +function getKeys(node: TreeNode): string[] | number[] { + const schema = Tree.schema(node); + if (schema.kind === NodeKind.Array) { + const arrayNode = node as TreeArrayNode; + const keys: number[] = []; + for (let index = 0; index < arrayNode.length; index++) { + keys.push(index); + } + return keys; + } + if (schema.kind === NodeKind.Map) { + return [...(node as TreeMapNode).keys()]; + } + + return Object.keys(node); +} +``` diff --git a/packages/dds/tree/api-report/tree.alpha.api.md b/packages/dds/tree/api-report/tree.alpha.api.md index 70f2364b0522..e6e6fe2ec539 100644 --- a/packages/dds/tree/api-report/tree.alpha.api.md +++ b/packages/dds/tree/api-report/tree.alpha.api.md @@ -390,17 +390,17 @@ export interface SchemaCompatibilityStatus { // @public @sealed export class SchemaFactory { constructor(scope: TScope); - array(allowedTypes: T): TreeNodeSchema`>, NodeKind.Array, TreeArrayNode & WithType`>>, Iterable>, true, T>; - array(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Array, TreeArrayNode & WithType>, Iterable>, true, T>; - arrayRecursive>(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Array, TreeArrayNodeUnsafe & WithType>, { + array(allowedTypes: T): TreeNodeSchema`>, NodeKind.Array, TreeArrayNode & WithType`>, NodeKind.Array>, Iterable>, true, T>; + array(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Array, TreeArrayNode & WithType, NodeKind.Array>, Iterable>, true, T>; + arrayRecursive>(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Array, TreeArrayNodeUnsafe & WithType, NodeKind.Array>, { [Symbol.iterator](): Iterator>; }, false, T>; readonly boolean: TreeNodeSchema<"com.fluidframework.leaf.boolean", NodeKind.Leaf, boolean, boolean>; readonly handle: TreeNodeSchema<"com.fluidframework.leaf.handle", NodeKind.Leaf, IFluidHandle, IFluidHandle>; get identifier(): FieldSchema; - map(allowedTypes: T): TreeNodeSchema`>, NodeKind.Map, TreeMapNode & WithType`>>, MapNodeInsertableData, true, T>; - map(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Map, TreeMapNode & WithType>, MapNodeInsertableData, true, T>; - mapRecursive>(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Map, TreeMapNodeUnsafe & WithType>, { + map(allowedTypes: T): TreeNodeSchema`>, NodeKind.Map, TreeMapNode & WithType`>, NodeKind.Map>, MapNodeInsertableData, true, T>; + map(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Map, TreeMapNode & WithType, NodeKind.Map>, MapNodeInsertableData, true, T>; + mapRecursive>(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Map, TreeMapNodeUnsafe & WithType, NodeKind.Map>, { [Symbol.iterator](): Iterator<[ string, InsertableTreeNodeFromImplicitAllowedTypesUnsafe @@ -504,7 +504,9 @@ export interface TreeMapNodeUnsafe> e export abstract class TreeNode implements WithType { static [Symbol.hasInstance](value: unknown): value is TreeNode; static [Symbol.hasInstance] TreeNode>(this: TSchema, value: unknown): value is InstanceType; + // @deprecated abstract get [typeNameSymbol](): string; + abstract get [typeSchemaSymbol](): TreeNodeSchemaClass; protected constructor(token: unknown); } @@ -550,10 +552,10 @@ interface TreeNodeSchemaNonClass, TypeName extends string = string> = TreeNode & ObjectFromSchemaRecord & WithType; +export type TreeObjectNode, TypeName extends string = string> = TreeNode & ObjectFromSchemaRecord & WithType; // @public -export type TreeObjectNodeUnsafe>, TypeName extends string = string> = TreeNode & ObjectFromSchemaRecordUnsafe & WithType; +export type TreeObjectNodeUnsafe>, TypeName extends string = string> = TreeNode & ObjectFromSchemaRecordUnsafe & WithType; // @public export enum TreeStatus { @@ -590,9 +592,12 @@ export interface TreeViewEvents { schemaChanged(): void; } -// @public +// @public @deprecated const typeNameSymbol: unique symbol; +// @public +export const typeSchemaSymbol: unique symbol; + // @public export type Unenforced<_DesiredExtendsConstraint> = unknown; @@ -600,7 +605,7 @@ export type Unenforced<_DesiredExtendsConstraint> = unknown; export type Unhydrated = T; // @public -export type ValidateRecursiveSchema, { +export type ValidateRecursiveSchema, { [NodeKind.Object]: T["info"] extends RestrictiveReadonlyRecord ? InsertableObjectFromSchemaRecord : unknown; [NodeKind.Array]: T["info"] extends ImplicitAllowedTypes ? Iterable> : unknown; [NodeKind.Map]: T["info"] extends ImplicitAllowedTypes ? Iterable<[string, InsertableTreeNodeFromImplicitAllowedTypes]> : unknown; @@ -611,8 +616,10 @@ export type ValidateRecursiveSchema> = true; // @public @sealed -export interface WithType { - get [typeNameSymbol](): TName; +export interface WithType { + // @deprecated + get [typeNameSymbol](): Name; + get [typeSchemaSymbol](): TreeNodeSchemaClass; } // (No @packageDocumentation comment for this package) diff --git a/packages/dds/tree/api-report/tree.beta.api.md b/packages/dds/tree/api-report/tree.beta.api.md index a804ffaf5280..e62870775fd4 100644 --- a/packages/dds/tree/api-report/tree.beta.api.md +++ b/packages/dds/tree/api-report/tree.beta.api.md @@ -322,17 +322,17 @@ export interface SchemaCompatibilityStatus { // @public @sealed export class SchemaFactory { constructor(scope: TScope); - array(allowedTypes: T): TreeNodeSchema`>, NodeKind.Array, TreeArrayNode & WithType`>>, Iterable>, true, T>; - array(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Array, TreeArrayNode & WithType>, Iterable>, true, T>; - arrayRecursive>(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Array, TreeArrayNodeUnsafe & WithType>, { + array(allowedTypes: T): TreeNodeSchema`>, NodeKind.Array, TreeArrayNode & WithType`>, NodeKind.Array>, Iterable>, true, T>; + array(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Array, TreeArrayNode & WithType, NodeKind.Array>, Iterable>, true, T>; + arrayRecursive>(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Array, TreeArrayNodeUnsafe & WithType, NodeKind.Array>, { [Symbol.iterator](): Iterator>; }, false, T>; readonly boolean: TreeNodeSchema<"com.fluidframework.leaf.boolean", NodeKind.Leaf, boolean, boolean>; readonly handle: TreeNodeSchema<"com.fluidframework.leaf.handle", NodeKind.Leaf, IFluidHandle, IFluidHandle>; get identifier(): FieldSchema; - map(allowedTypes: T): TreeNodeSchema`>, NodeKind.Map, TreeMapNode & WithType`>>, MapNodeInsertableData, true, T>; - map(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Map, TreeMapNode & WithType>, MapNodeInsertableData, true, T>; - mapRecursive>(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Map, TreeMapNodeUnsafe & WithType>, { + map(allowedTypes: T): TreeNodeSchema`>, NodeKind.Map, TreeMapNode & WithType`>, NodeKind.Map>, MapNodeInsertableData, true, T>; + map(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Map, TreeMapNode & WithType, NodeKind.Map>, MapNodeInsertableData, true, T>; + mapRecursive>(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Map, TreeMapNodeUnsafe & WithType, NodeKind.Map>, { [Symbol.iterator](): Iterator<[ string, InsertableTreeNodeFromImplicitAllowedTypesUnsafe @@ -436,7 +436,9 @@ export interface TreeMapNodeUnsafe> e export abstract class TreeNode implements WithType { static [Symbol.hasInstance](value: unknown): value is TreeNode; static [Symbol.hasInstance] TreeNode>(this: TSchema, value: unknown): value is InstanceType; + // @deprecated abstract get [typeNameSymbol](): string; + abstract get [typeSchemaSymbol](): TreeNodeSchemaClass; protected constructor(token: unknown); } @@ -482,10 +484,10 @@ interface TreeNodeSchemaNonClass, TypeName extends string = string> = TreeNode & ObjectFromSchemaRecord & WithType; +export type TreeObjectNode, TypeName extends string = string> = TreeNode & ObjectFromSchemaRecord & WithType; // @public -export type TreeObjectNodeUnsafe>, TypeName extends string = string> = TreeNode & ObjectFromSchemaRecordUnsafe & WithType; +export type TreeObjectNodeUnsafe>, TypeName extends string = string> = TreeNode & ObjectFromSchemaRecordUnsafe & WithType; // @public export enum TreeStatus { @@ -522,9 +524,12 @@ export interface TreeViewEvents { schemaChanged(): void; } -// @public +// @public @deprecated const typeNameSymbol: unique symbol; +// @public +export const typeSchemaSymbol: unique symbol; + // @public export type Unenforced<_DesiredExtendsConstraint> = unknown; @@ -532,7 +537,7 @@ export type Unenforced<_DesiredExtendsConstraint> = unknown; export type Unhydrated = T; // @public -export type ValidateRecursiveSchema, { +export type ValidateRecursiveSchema, { [NodeKind.Object]: T["info"] extends RestrictiveReadonlyRecord ? InsertableObjectFromSchemaRecord : unknown; [NodeKind.Array]: T["info"] extends ImplicitAllowedTypes ? Iterable> : unknown; [NodeKind.Map]: T["info"] extends ImplicitAllowedTypes ? Iterable<[string, InsertableTreeNodeFromImplicitAllowedTypes]> : unknown; @@ -543,8 +548,10 @@ export type ValidateRecursiveSchema> = true; // @public @sealed -export interface WithType { - get [typeNameSymbol](): TName; +export interface WithType { + // @deprecated + get [typeNameSymbol](): Name; + get [typeSchemaSymbol](): TreeNodeSchemaClass; } // (No @packageDocumentation comment for this package) diff --git a/packages/dds/tree/api-report/tree.public.api.md b/packages/dds/tree/api-report/tree.public.api.md index 1d40cd22a9f0..977bbfa4f8e2 100644 --- a/packages/dds/tree/api-report/tree.public.api.md +++ b/packages/dds/tree/api-report/tree.public.api.md @@ -322,17 +322,17 @@ export interface SchemaCompatibilityStatus { // @public @sealed export class SchemaFactory { constructor(scope: TScope); - array(allowedTypes: T): TreeNodeSchema`>, NodeKind.Array, TreeArrayNode & WithType`>>, Iterable>, true, T>; - array(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Array, TreeArrayNode & WithType>, Iterable>, true, T>; - arrayRecursive>(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Array, TreeArrayNodeUnsafe & WithType>, { + array(allowedTypes: T): TreeNodeSchema`>, NodeKind.Array, TreeArrayNode & WithType`>, NodeKind.Array>, Iterable>, true, T>; + array(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Array, TreeArrayNode & WithType, NodeKind.Array>, Iterable>, true, T>; + arrayRecursive>(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Array, TreeArrayNodeUnsafe & WithType, NodeKind.Array>, { [Symbol.iterator](): Iterator>; }, false, T>; readonly boolean: TreeNodeSchema<"com.fluidframework.leaf.boolean", NodeKind.Leaf, boolean, boolean>; readonly handle: TreeNodeSchema<"com.fluidframework.leaf.handle", NodeKind.Leaf, IFluidHandle, IFluidHandle>; get identifier(): FieldSchema; - map(allowedTypes: T): TreeNodeSchema`>, NodeKind.Map, TreeMapNode & WithType`>>, MapNodeInsertableData, true, T>; - map(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Map, TreeMapNode & WithType>, MapNodeInsertableData, true, T>; - mapRecursive>(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Map, TreeMapNodeUnsafe & WithType>, { + map(allowedTypes: T): TreeNodeSchema`>, NodeKind.Map, TreeMapNode & WithType`>, NodeKind.Map>, MapNodeInsertableData, true, T>; + map(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Map, TreeMapNode & WithType, NodeKind.Map>, MapNodeInsertableData, true, T>; + mapRecursive>(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Map, TreeMapNodeUnsafe & WithType, NodeKind.Map>, { [Symbol.iterator](): Iterator<[ string, InsertableTreeNodeFromImplicitAllowedTypesUnsafe @@ -436,7 +436,9 @@ export interface TreeMapNodeUnsafe> e export abstract class TreeNode implements WithType { static [Symbol.hasInstance](value: unknown): value is TreeNode; static [Symbol.hasInstance] TreeNode>(this: TSchema, value: unknown): value is InstanceType; + // @deprecated abstract get [typeNameSymbol](): string; + abstract get [typeSchemaSymbol](): TreeNodeSchemaClass; protected constructor(token: unknown); } @@ -482,10 +484,10 @@ interface TreeNodeSchemaNonClass, TypeName extends string = string> = TreeNode & ObjectFromSchemaRecord & WithType; +export type TreeObjectNode, TypeName extends string = string> = TreeNode & ObjectFromSchemaRecord & WithType; // @public -export type TreeObjectNodeUnsafe>, TypeName extends string = string> = TreeNode & ObjectFromSchemaRecordUnsafe & WithType; +export type TreeObjectNodeUnsafe>, TypeName extends string = string> = TreeNode & ObjectFromSchemaRecordUnsafe & WithType; // @public export enum TreeStatus { @@ -522,9 +524,12 @@ export interface TreeViewEvents { schemaChanged(): void; } -// @public +// @public @deprecated const typeNameSymbol: unique symbol; +// @public +export const typeSchemaSymbol: unique symbol; + // @public export type Unenforced<_DesiredExtendsConstraint> = unknown; @@ -532,7 +537,7 @@ export type Unenforced<_DesiredExtendsConstraint> = unknown; export type Unhydrated = T; // @public -export type ValidateRecursiveSchema, { +export type ValidateRecursiveSchema, { [NodeKind.Object]: T["info"] extends RestrictiveReadonlyRecord ? InsertableObjectFromSchemaRecord : unknown; [NodeKind.Array]: T["info"] extends ImplicitAllowedTypes ? Iterable> : unknown; [NodeKind.Map]: T["info"] extends ImplicitAllowedTypes ? Iterable<[string, InsertableTreeNodeFromImplicitAllowedTypes]> : unknown; @@ -543,8 +548,10 @@ export type ValidateRecursiveSchema> = true; // @public @sealed -export interface WithType { - get [typeNameSymbol](): TName; +export interface WithType { + // @deprecated + get [typeNameSymbol](): Name; + get [typeSchemaSymbol](): TreeNodeSchemaClass; } // (No @packageDocumentation comment for this package) diff --git a/packages/dds/tree/src/index.ts b/packages/dds/tree/src/index.ts index 335ebbb89fae..284a72884ece 100644 --- a/packages/dds/tree/src/index.ts +++ b/packages/dds/tree/src/index.ts @@ -92,6 +92,7 @@ export { type SchemaCompatibilityStatus, type FieldProps, type InternalTreeNode, + type WithType, // Types not really intended for public use, but used in links. // Can not be moved to internalTypes since doing so causes app code to throw errors like: // Error: src/simple-tree/objectNode.ts:72:1 - (ae-unresolved-link) The @link reference could not be resolved: The package "@fluidframework/tree" does not have an export "TreeNodeApi" @@ -101,7 +102,6 @@ export { // Can not be moved to internalTypes since doing so causes app code to throw errors like: // error TS2742: The inferred type of 'Inventory' cannot be named without a reference to '../node_modules/@fluidframework/tree/lib/internalTypes.js'. This is likely not portable. A type annotation is necessary. type AllowedTypes, - type WithType, type TreeObjectNodeUnsafe, type InsertableTreeNodeFromImplicitAllowedTypesUnsafe, type TreeArrayNodeUnsafe, @@ -109,6 +109,8 @@ export { type InsertableObjectFromSchemaRecordUnsafe, type InsertableTreeFieldFromImplicitFieldUnsafe, type FieldSchemaUnsafe, + // System types (not in Internal types for various reasons, like doc links or cannot be named errors). + getJsonSchema, // Recursive Schema APIs type ValidateRecursiveSchema, type FixRecursiveArraySchema, @@ -122,6 +124,7 @@ export { test_RecursiveObject, test_RecursiveObject_base, test_RecursiveObjectPojoMode, + // Back to normal types type JsonTreeSchema, type JsonSchemaId, type JsonNodeSchema, @@ -135,7 +138,7 @@ export { type JsonRefPath, type JsonSchemaType, type JsonLeafSchemaType, - getJsonSchema, + type typeSchemaSymbol, } from "./simple-tree/index.js"; export { SharedTree, configuredSharedTree } from "./treeFactory.js"; diff --git a/packages/dds/tree/src/simple-tree/api/schemaFactory.ts b/packages/dds/tree/src/simple-tree/api/schemaFactory.ts index 462064575a5e..8914490286e3 100644 --- a/packages/dds/tree/src/simple-tree/api/schemaFactory.ts +++ b/packages/dds/tree/src/simple-tree/api/schemaFactory.ts @@ -303,7 +303,7 @@ export class SchemaFactory< ): TreeNodeSchema< ScopedSchemaName`>, NodeKind.Map, - TreeMapNode & WithType`>>, + TreeMapNode & WithType`>, NodeKind.Map>, MapNodeInsertableData, true, T @@ -325,7 +325,7 @@ export class SchemaFactory< ): TreeNodeSchemaClass< ScopedSchemaName, NodeKind.Map, - TreeMapNode & WithType>, + TreeMapNode & WithType, NodeKind.Map>, MapNodeInsertableData, true, T @@ -377,7 +377,7 @@ export class SchemaFactory< ): TreeNodeSchemaClass< ScopedSchemaName, NodeKind.Map, - TreeMapNode & WithType>, + TreeMapNode & WithType, NodeKind.Map>, MapNodeInsertableData, ImplicitlyConstructable, T @@ -429,7 +429,7 @@ export class SchemaFactory< ): TreeNodeSchema< ScopedSchemaName`>, NodeKind.Array, - TreeArrayNode & WithType`>>, + TreeArrayNode & WithType`>, NodeKind.Array>, Iterable>, true, T @@ -453,7 +453,7 @@ export class SchemaFactory< ): TreeNodeSchemaClass< ScopedSchemaName, NodeKind.Array, - TreeArrayNode & WithType>, + TreeArrayNode & WithType, NodeKind.Array>, Iterable>, true, T @@ -508,7 +508,7 @@ export class SchemaFactory< ): TreeNodeSchemaClass< ScopedSchemaName, NodeKind.Array, - TreeArrayNode & WithType>, + TreeArrayNode & WithType, NodeKind.Array>, Iterable>, ImplicitlyConstructable, T @@ -663,7 +663,7 @@ export class SchemaFactory< return RecursiveArray as TreeNodeSchemaClass< ScopedSchemaName, NodeKind.Array, - TreeArrayNodeUnsafe & WithType>, + TreeArrayNodeUnsafe & WithType, NodeKind.Array>, { /** * Iterator for the iterable of content for this node. @@ -705,7 +705,7 @@ export class SchemaFactory< return MapSchema as TreeNodeSchemaClass< ScopedSchemaName, NodeKind.Map, - TreeMapNodeUnsafe & WithType>, + TreeMapNodeUnsafe & WithType, NodeKind.Map>, { /** * Iterator for the iterable of content for this node. diff --git a/packages/dds/tree/src/simple-tree/api/schemaFactoryRecursive.ts b/packages/dds/tree/src/simple-tree/api/schemaFactoryRecursive.ts index 36f2ec4517c0..2630177273e2 100644 --- a/packages/dds/tree/src/simple-tree/api/schemaFactoryRecursive.ts +++ b/packages/dds/tree/src/simple-tree/api/schemaFactoryRecursive.ts @@ -127,7 +127,7 @@ export type ValidateRecursiveSchema< // NodeKind: These are the NodeKinds which currently can be used recursively. NodeKind.Array | NodeKind.Map | NodeKind.Object, // TNode: The produced node API. This is pretty minimal validation: more could be added if similar to how TInsertable works below if needed. - TreeNode & WithType, + TreeNode & WithType, // TInsertable: What can be passed to the constructor. This should be enough to catch most issues with incorrect schema. // These match whats defined in the recursive methods on `SchemaFactory` except they do not use `Unenforced`. { diff --git a/packages/dds/tree/src/simple-tree/arrayNode.ts b/packages/dds/tree/src/simple-tree/arrayNode.ts index 32f8a8f87990..fa5b0172ec0a 100644 --- a/packages/dds/tree/src/simple-tree/arrayNode.ts +++ b/packages/dds/tree/src/simple-tree/arrayNode.ts @@ -31,12 +31,14 @@ import type { } from "./schemaTypes.js"; import { type WithType, + // eslint-disable-next-line import/no-deprecated typeNameSymbol, NodeKind, type TreeNode, type InternalTreeNode, type TreeNodeSchemaClass, type TreeNodeSchema, + typeSchemaSymbol, } from "./core/index.js"; import { mapTreeFromNodeData } from "./toMapTree.js"; import { fail } from "../util/index.js"; @@ -890,6 +892,7 @@ abstract class CustomArrayNodeBase * * @param name - Unique identifier for this schema including the factory's scope. */ +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type export function arraySchema< TName extends string, const T extends ImplicitAllowedTypes, @@ -899,14 +902,15 @@ export function arraySchema< info: T, implicitlyConstructable: ImplicitlyConstructable, customizable: boolean, -): TreeNodeSchemaClass< - TName, - NodeKind.Array, - TreeArrayNode & WithType, - Iterable>, - ImplicitlyConstructable, - T -> { +) { + type Output = TreeNodeSchemaClass< + TName, + NodeKind.Array, + TreeArrayNode & WithType, + Iterable>, + ImplicitlyConstructable, + T + >; let flexSchema: FlexFieldNodeSchema; // This class returns a proxy from its constructor to handle numeric indexing. @@ -979,16 +983,21 @@ export function arraySchema< public static readonly implicitlyConstructable: ImplicitlyConstructable = implicitlyConstructable; + // eslint-disable-next-line import/no-deprecated public get [typeNameSymbol](): TName { return identifier; } + public get [typeSchemaSymbol](): Output { + return schema.constructorCached?.constructor as unknown as Output; + } protected get simpleSchema(): T { return info; } } - return schema; + const output: Output = schema; + return output; } function validateSafeInteger(index: number): void { diff --git a/packages/dds/tree/src/simple-tree/core/index.ts b/packages/dds/tree/src/simple-tree/core/index.ts index bc9a521bebf0..16fcbc12e406 100644 --- a/packages/dds/tree/src/simple-tree/core/index.ts +++ b/packages/dds/tree/src/simple-tree/core/index.ts @@ -9,7 +9,7 @@ export { getKernel, tryGetTreeNodeSchema, } from "./treeNodeKernel.js"; -export { type WithType, typeNameSymbol } from "./withType.js"; +export { type WithType, typeNameSymbol, typeSchemaSymbol } from "./withType.js"; export { type TreeChangeEvents, TreeNode, diff --git a/packages/dds/tree/src/simple-tree/core/types.ts b/packages/dds/tree/src/simple-tree/core/types.ts index c83f36454d7a..babd500fd534 100644 --- a/packages/dds/tree/src/simple-tree/core/types.ts +++ b/packages/dds/tree/src/simple-tree/core/types.ts @@ -7,8 +7,9 @@ import { assert } from "@fluidframework/core-utils/internal"; import { UsageError } from "@fluidframework/telemetry-utils/internal"; import type { ErasedType } from "@fluidframework/core-interfaces"; -import { NodeKind } from "./treeNodeSchema.js"; -import { type WithType, typeNameSymbol } from "./withType.js"; +import { NodeKind, type TreeNodeSchemaClass } from "./treeNodeSchema.js"; +// eslint-disable-next-line import/no-deprecated +import { type WithType, typeNameSymbol, type typeSchemaSymbol } from "./withType.js"; import { tryGetTreeNodeSchema } from "./treeNodeKernel.js"; import { isFlexTreeNode, type FlexTreeNode } from "../../feature-libraries/index.js"; @@ -154,9 +155,18 @@ export abstract class TreeNode implements WithType { * Adds a type symbol for stronger typing. * @privateRemarks * Subclasses provide more specific strings for this to get strong typing of otherwise type compatible nodes. + * @deprecated Use {@link typeSchemaSymbol} instead. */ + // eslint-disable-next-line import/no-deprecated public abstract get [typeNameSymbol](): string; + /** + * Adds a type symbol for stronger typing. + * @privateRemarks + * Subclasses provide more specific strings for this to get strong typing of otherwise type compatible nodes. + */ + public abstract get [typeSchemaSymbol](): TreeNodeSchemaClass; + /** * Provides `instanceof` support for testing if a value is a `TreeNode`. * @remarks diff --git a/packages/dds/tree/src/simple-tree/core/withType.ts b/packages/dds/tree/src/simple-tree/core/withType.ts index ef5a0be6638e..0dda6d58289a 100644 --- a/packages/dds/tree/src/simple-tree/core/withType.ts +++ b/packages/dds/tree/src/simple-tree/core/withType.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. */ +import type { NodeKind, TreeNodeSchemaClass } from "./treeNodeSchema.js"; + /** * The type of a {@link TreeNode}. * For more information about the type, use `Tree.schema(theNode)` instead. @@ -14,19 +16,44 @@ * Instead construct a real node of the desired type using its constructor. * @privateRemarks * This prevents non-nodes from being accidentally used as nodes, as well as allows the type checker to distinguish different node types. - * @public + * @deprecated External code should use `Tree.schema(theNode)` for runtime data access, and for typechecking and internally {@link typeSchemaSymbol} provides a superset of this functionality. + * @system @public */ export const typeNameSymbol: unique symbol = Symbol("TreeNode Type"); +/** + * The type of a {@link TreeNode}. + * For more information about the type, use `Tree.schema(theNode)` instead. + * @remarks + * This symbol mainly exists on nodes to allow TypeScript to provide more accurate type checking. + * `Tree.is` and `Tree.schema` provide a superset of this information in more friendly ways. + * + * This symbol should not manually be added to objects as doing so allows the object to be invalidly used where nodes are expected. + * Instead construct a real node of the desired type using its constructor. + * @privateRemarks + * This prevents non-nodes from being accidentally used as nodes, as well as allows the type checker to distinguish different node types. + * @system @public + */ +export const typeSchemaSymbol: unique symbol = Symbol("TreeNode Schema"); + /** * Adds a type symbol to a type for stronger typing. * @remarks * An implementation detail of {@link TreeNode}'s strong typing setup: not intended for direct use outside of this package. * @sealed @public */ -export interface WithType { +export interface WithType< + out Name extends string = string, + out Kind extends NodeKind = NodeKind, +> { + /** + * Type symbol, marking a type in a way to increase type safety via strong type checking. + * @deprecated Use {@link typeSchemaSymbol} instead. + */ + get [typeNameSymbol](): Name; + /** * Type symbol, marking a type in a way to increase type safety via strong type checking. */ - get [typeNameSymbol](): TName; + get [typeSchemaSymbol](): TreeNodeSchemaClass; } diff --git a/packages/dds/tree/src/simple-tree/index.ts b/packages/dds/tree/src/simple-tree/index.ts index d74e686468e2..37f484700ad3 100644 --- a/packages/dds/tree/src/simple-tree/index.ts +++ b/packages/dds/tree/src/simple-tree/index.ts @@ -5,6 +5,7 @@ export { typeNameSymbol, + typeSchemaSymbol, type WithType, type TreeNodeSchema, NodeKind, diff --git a/packages/dds/tree/src/simple-tree/mapNode.ts b/packages/dds/tree/src/simple-tree/mapNode.ts index 00d67412451f..c207bae652f6 100644 --- a/packages/dds/tree/src/simple-tree/mapNode.ts +++ b/packages/dds/tree/src/simple-tree/mapNode.ts @@ -30,8 +30,10 @@ import { type TreeNodeSchemaClass, type TreeNodeSchema, type WithType, + // eslint-disable-next-line import/no-deprecated typeNameSymbol, type TreeNode, + typeSchemaSymbol, } from "./core/index.js"; import { mapTreeFromNodeData } from "./toMapTree.js"; import { getFlexSchema } from "./toFlexSchema.js"; @@ -253,14 +255,18 @@ export function mapSchema< public static readonly implicitlyConstructable: ImplicitlyConstructable = implicitlyConstructable; + // eslint-disable-next-line import/no-deprecated public get [typeNameSymbol](): TName { return identifier; } + public get [typeSchemaSymbol](): typeof schemaErased { + return schema.constructorCached?.constructor as unknown as typeof schemaErased; + } } const schemaErased: TreeNodeSchemaClass< TName, NodeKind.Map, - TreeMapNode & WithType, + TreeMapNode & WithType, MapNodeInsertableData, ImplicitlyConstructable, T diff --git a/packages/dds/tree/src/simple-tree/objectNode.ts b/packages/dds/tree/src/simple-tree/objectNode.ts index a74e647b00b2..a6a84641ea3b 100644 --- a/packages/dds/tree/src/simple-tree/objectNode.ts +++ b/packages/dds/tree/src/simple-tree/objectNode.ts @@ -41,7 +41,9 @@ import { type TreeNodeSchema, NodeKind, type WithType, + // eslint-disable-next-line import/no-deprecated typeNameSymbol, + typeSchemaSymbol, type InternalTreeNode, type TreeNode, } from "./core/index.js"; @@ -75,7 +77,7 @@ export type ObjectFromSchemaRecord< export type TreeObjectNode< T extends RestrictiveReadonlyRecord, TypeName extends string = string, -> = TreeNode & ObjectFromSchemaRecord & WithType; +> = TreeNode & ObjectFromSchemaRecord & WithType; /** * Type utility for determining whether or not an implicit field schema has a default value. @@ -414,15 +416,19 @@ export function objectSchema< public static readonly implicitlyConstructable: ImplicitlyConstructable = implicitlyConstructable; + // eslint-disable-next-line import/no-deprecated public get [typeNameSymbol](): TName { return identifier; } + public get [typeSchemaSymbol](): Output { + return CustomObjectNode.constructorCached?.constructor as unknown as Output; + } } - - return CustomObjectNode as typeof CustomObjectNode & + type Output = typeof CustomObjectNode & (new ( input: InsertableObjectFromSchemaRecord | InternalTreeNode, ) => TreeObjectNode); + return CustomObjectNode as Output; } const targetToProxy: WeakMap = new WeakMap(); diff --git a/packages/dds/tree/src/simple-tree/typesUnsafe.ts b/packages/dds/tree/src/simple-tree/typesUnsafe.ts index fc950c7c672e..30088087d7f5 100644 --- a/packages/dds/tree/src/simple-tree/typesUnsafe.ts +++ b/packages/dds/tree/src/simple-tree/typesUnsafe.ts @@ -58,7 +58,7 @@ export type ObjectFromSchemaRecordUnsafe< export type TreeObjectNodeUnsafe< T extends Unenforced>, TypeName extends string = string, -> = TreeNode & ObjectFromSchemaRecordUnsafe & WithType; +> = TreeNode & ObjectFromSchemaRecordUnsafe & WithType; /** * {@link Unenforced} version of {@link TreeFieldFromImplicitField}. 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 3c41efc1e793..7c9c22a09865 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 @@ -17,13 +17,18 @@ import { TreeStatus } from "../../../feature-libraries/index.js"; import { treeNodeApi as Tree, TreeViewConfiguration, + type TreeArrayNode, + type TreeMapNode, type TreeView, } from "../../../simple-tree/index.js"; import { type TreeNodeSchema, + type WithType, isTreeNode, + NodeKind, // Import directly to get the non-type import to allow testing of the package only instanceof TreeNode, + typeSchemaSymbol, // eslint-disable-next-line import/no-internal-modules } from "../../../simple-tree/core/index.js"; import { @@ -955,4 +960,73 @@ describe("schemaFactory", () => { ); }); }); + + it("kind based narrowing", () => { + const factory = new SchemaFactory(""); + + class Obj extends factory.object("O", {}) {} + class Arr extends factory.array("A", []) {} + class MapNode extends factory.map("M", []) {} + + const obj = hydrate(Obj, {}); + const arr = hydrate(Arr, []); + const mapNode = hydrate(MapNode, {}); + + function f(node: TreeNode & WithType): "object"; + function f(node: TreeNode & WithType): "array"; + function f(node: TreeNode & WithType): "map"; + function f(node: TreeNode): "any"; + + function f(node: TreeNode): string { + return "nope"; + } + + // Compile time check that NodeKind based overload resolution works as expected. + const s1: "object" = f(obj); + const s2: "array" = f(arr); + const s3: "map" = f(mapNode); + const s4: "any" = f(obj as TreeNode); + + // Check runtime data: + assert.equal(obj[typeSchemaSymbol], Obj); + assert.equal(arr[typeSchemaSymbol], Arr); + assert.equal(mapNode[typeSchemaSymbol], MapNode); + }); + + it("kind based narrowing example", () => { + const factory = new SchemaFactory(""); + + class Obj extends factory.object("O", { a: factory.number }) {} + class Arr extends factory.array("A", [factory.number]) {} + class MapNode extends factory.map("M", [factory.number]) {} + + const obj = hydrate(Obj, { a: 5 }); + const arr = hydrate(Arr, [5]); + const mapNode = hydrate(MapNode, { x: 5 }); + + assert.deepEqual(getKeys(obj), ["a"]); + assert.deepEqual(getKeys(arr), [0]); + assert.deepEqual(getKeys(mapNode), ["x"]); + }); }); + +// kind based narrowing example +function getKeys(node: TreeNode & WithType): number[]; +function getKeys(node: TreeNode & WithType): string[]; +function getKeys(node: TreeNode): string[] | number[]; +function getKeys(node: TreeNode): string[] | number[] { + const schema = Tree.schema(node); + if (schema.kind === NodeKind.Array) { + const arrayNode = node as TreeArrayNode; + const keys: number[] = []; + for (let index = 0; index < arrayNode.length; index++) { + keys.push(index); + } + return keys; + } + if (schema.kind === NodeKind.Map) { + return [...(node as TreeMapNode).keys()]; + } + + return Object.keys(node); +} diff --git a/packages/dds/tree/src/test/simple-tree/core/types.spec.ts b/packages/dds/tree/src/test/simple-tree/core/types.spec.ts index 59b0e4e191b7..ba6a8b395816 100644 --- a/packages/dds/tree/src/test/simple-tree/core/types.spec.ts +++ b/packages/dds/tree/src/test/simple-tree/core/types.spec.ts @@ -21,6 +21,7 @@ import { NodeKind, type TreeNodeSchema, typeNameSymbol, + typeSchemaSymbol, // Used to test that TreeNode is a type only export. TreeNode as TreeNodePublic, } from "../../../simple-tree/index.js"; @@ -63,6 +64,9 @@ describe("simple-tree types", () => { public override get [typeNameSymbol](): string { throw new Error("Method not implemented."); } + public override get [typeSchemaSymbol](): never { + throw new Error("Method not implemented."); + } public constructor() { super({}); } @@ -164,6 +168,9 @@ describe("simple-tree types", () => { public override get [typeNameSymbol](): string { throw new Error("Method not implemented."); } + public override get [typeSchemaSymbol](): never { + throw new Error("Method not implemented."); + } public constructor(input: number | InternalTreeNode) { super(input); log.push("done"); @@ -201,6 +208,9 @@ describe("simple-tree types", () => { public override get [typeNameSymbol](): string { throw new Error("Method not implemented."); } + public override get [typeSchemaSymbol](): never { + throw new Error("Method not implemented."); + } } assert.throws( @@ -233,6 +243,9 @@ describe("simple-tree types", () => { public override get [typeNameSymbol](): string { throw new Error("Method not implemented."); } + public override get [typeSchemaSymbol](): never { + throw new Error("Method not implemented."); + } public constructor() { super(0); } @@ -280,6 +293,9 @@ describe("simple-tree types", () => { public override get [typeNameSymbol](): string { throw new Error("Method not implemented."); } + public override get [typeSchemaSymbol](): never { + throw new Error("Method not implemented."); + } public constructor() { super(0); } 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 a11bf338a322..c7460162f47c 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 @@ -672,17 +672,17 @@ export interface SchemaCompatibilityStatus { // @public @sealed export class SchemaFactory { constructor(scope: TScope); - array(allowedTypes: T): TreeNodeSchema`>, NodeKind.Array, TreeArrayNode & WithType`>>, Iterable>, true, T>; - array(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Array, TreeArrayNode & WithType>, Iterable>, true, T>; - arrayRecursive>(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Array, TreeArrayNodeUnsafe & WithType>, { + array(allowedTypes: T): TreeNodeSchema`>, NodeKind.Array, TreeArrayNode & WithType`>, NodeKind.Array>, Iterable>, true, T>; + array(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Array, TreeArrayNode & WithType, NodeKind.Array>, Iterable>, true, T>; + arrayRecursive>(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Array, TreeArrayNodeUnsafe & WithType, NodeKind.Array>, { [Symbol.iterator](): Iterator>; }, false, T>; readonly boolean: TreeNodeSchema<"com.fluidframework.leaf.boolean", NodeKind.Leaf, boolean, boolean>; readonly handle: TreeNodeSchema<"com.fluidframework.leaf.handle", NodeKind.Leaf, IFluidHandle, IFluidHandle>; get identifier(): FieldSchema; - map(allowedTypes: T): TreeNodeSchema`>, NodeKind.Map, TreeMapNode & WithType`>>, MapNodeInsertableData, true, T>; - map(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Map, TreeMapNode & WithType>, MapNodeInsertableData, true, T>; - mapRecursive>(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Map, TreeMapNodeUnsafe & WithType>, { + map(allowedTypes: T): TreeNodeSchema`>, NodeKind.Map, TreeMapNode & WithType`>, NodeKind.Map>, MapNodeInsertableData, true, T>; + map(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Map, TreeMapNode & WithType, NodeKind.Map>, MapNodeInsertableData, true, T>; + mapRecursive>(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Map, TreeMapNodeUnsafe & WithType, NodeKind.Map>, { [Symbol.iterator](): Iterator<[ string, InsertableTreeNodeFromImplicitAllowedTypesUnsafe @@ -808,7 +808,9 @@ export interface TreeMapNodeUnsafe> e export abstract class TreeNode implements WithType { static [Symbol.hasInstance](value: unknown): value is TreeNode; static [Symbol.hasInstance] TreeNode>(this: TSchema, value: unknown): value is InstanceType; + // @deprecated abstract get [typeNameSymbol](): string; + abstract get [typeSchemaSymbol](): TreeNodeSchemaClass; protected constructor(token: unknown); } @@ -854,10 +856,10 @@ interface TreeNodeSchemaNonClass, TypeName extends string = string> = TreeNode & ObjectFromSchemaRecord & WithType; +export type TreeObjectNode, TypeName extends string = string> = TreeNode & ObjectFromSchemaRecord & WithType; // @public -export type TreeObjectNodeUnsafe>, TypeName extends string = string> = TreeNode & ObjectFromSchemaRecordUnsafe & WithType; +export type TreeObjectNodeUnsafe>, TypeName extends string = string> = TreeNode & ObjectFromSchemaRecordUnsafe & WithType; // @public export enum TreeStatus { @@ -894,9 +896,12 @@ export interface TreeViewEvents { schemaChanged(): void; } -// @public +// @public @deprecated const typeNameSymbol: unique symbol; +// @public +export const typeSchemaSymbol: unique symbol; + // @public export type Unenforced<_DesiredExtendsConstraint> = unknown; @@ -904,7 +909,7 @@ export type Unenforced<_DesiredExtendsConstraint> = unknown; export type Unhydrated = T; // @public -export type ValidateRecursiveSchema, { +export type ValidateRecursiveSchema, { [NodeKind.Object]: T["info"] extends RestrictiveReadonlyRecord ? InsertableObjectFromSchemaRecord : unknown; [NodeKind.Array]: T["info"] extends ImplicitAllowedTypes ? Iterable> : unknown; [NodeKind.Map]: T["info"] extends ImplicitAllowedTypes ? Iterable<[string, InsertableTreeNodeFromImplicitAllowedTypes]> : unknown; @@ -915,8 +920,10 @@ export type ValidateRecursiveSchema> = true; // @public @sealed -export interface WithType { - get [typeNameSymbol](): TName; +export interface WithType { + // @deprecated + get [typeNameSymbol](): Name; + get [typeSchemaSymbol](): TreeNodeSchemaClass; } ``` diff --git a/packages/framework/fluid-framework/api-report/fluid-framework.legacy.alpha.api.md b/packages/framework/fluid-framework/api-report/fluid-framework.legacy.alpha.api.md index f960abc30ca2..d6f9b977cb0b 100644 --- a/packages/framework/fluid-framework/api-report/fluid-framework.legacy.alpha.api.md +++ b/packages/framework/fluid-framework/api-report/fluid-framework.legacy.alpha.api.md @@ -978,17 +978,17 @@ export interface SchemaCompatibilityStatus { // @public @sealed export class SchemaFactory { constructor(scope: TScope); - array(allowedTypes: T): TreeNodeSchema`>, NodeKind.Array, TreeArrayNode & WithType`>>, Iterable>, true, T>; - array(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Array, TreeArrayNode & WithType>, Iterable>, true, T>; - arrayRecursive>(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Array, TreeArrayNodeUnsafe & WithType>, { + array(allowedTypes: T): TreeNodeSchema`>, NodeKind.Array, TreeArrayNode & WithType`>, NodeKind.Array>, Iterable>, true, T>; + array(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Array, TreeArrayNode & WithType, NodeKind.Array>, Iterable>, true, T>; + arrayRecursive>(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Array, TreeArrayNodeUnsafe & WithType, NodeKind.Array>, { [Symbol.iterator](): Iterator>; }, false, T>; readonly boolean: TreeNodeSchema<"com.fluidframework.leaf.boolean", NodeKind.Leaf, boolean, boolean>; readonly handle: TreeNodeSchema<"com.fluidframework.leaf.handle", NodeKind.Leaf, IFluidHandle, IFluidHandle>; get identifier(): FieldSchema; - map(allowedTypes: T): TreeNodeSchema`>, NodeKind.Map, TreeMapNode & WithType`>>, MapNodeInsertableData, true, T>; - map(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Map, TreeMapNode & WithType>, MapNodeInsertableData, true, T>; - mapRecursive>(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Map, TreeMapNodeUnsafe & WithType>, { + map(allowedTypes: T): TreeNodeSchema`>, NodeKind.Map, TreeMapNode & WithType`>, NodeKind.Map>, MapNodeInsertableData, true, T>; + map(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Map, TreeMapNode & WithType, NodeKind.Map>, MapNodeInsertableData, true, T>; + mapRecursive>(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Map, TreeMapNodeUnsafe & WithType, NodeKind.Map>, { [Symbol.iterator](): Iterator<[ string, InsertableTreeNodeFromImplicitAllowedTypesUnsafe @@ -1205,7 +1205,9 @@ export interface TreeMapNodeUnsafe> e export abstract class TreeNode implements WithType { static [Symbol.hasInstance](value: unknown): value is TreeNode; static [Symbol.hasInstance] TreeNode>(this: TSchema, value: unknown): value is InstanceType; + // @deprecated abstract get [typeNameSymbol](): string; + abstract get [typeSchemaSymbol](): TreeNodeSchemaClass; protected constructor(token: unknown); } @@ -1251,10 +1253,10 @@ interface TreeNodeSchemaNonClass, TypeName extends string = string> = TreeNode & ObjectFromSchemaRecord & WithType; +export type TreeObjectNode, TypeName extends string = string> = TreeNode & ObjectFromSchemaRecord & WithType; // @public -export type TreeObjectNodeUnsafe>, TypeName extends string = string> = TreeNode & ObjectFromSchemaRecordUnsafe & WithType; +export type TreeObjectNodeUnsafe>, TypeName extends string = string> = TreeNode & ObjectFromSchemaRecordUnsafe & WithType; // @public export enum TreeStatus { @@ -1291,9 +1293,12 @@ export interface TreeViewEvents { schemaChanged(): void; } -// @public +// @public @deprecated const typeNameSymbol: unique symbol; +// @public +export const typeSchemaSymbol: unique symbol; + // @public export type Unenforced<_DesiredExtendsConstraint> = unknown; @@ -1301,7 +1306,7 @@ export type Unenforced<_DesiredExtendsConstraint> = unknown; export type Unhydrated = T; // @public -export type ValidateRecursiveSchema, { +export type ValidateRecursiveSchema, { [NodeKind.Object]: T["info"] extends RestrictiveReadonlyRecord ? InsertableObjectFromSchemaRecord : unknown; [NodeKind.Array]: T["info"] extends ImplicitAllowedTypes ? Iterable> : unknown; [NodeKind.Map]: T["info"] extends ImplicitAllowedTypes ? Iterable<[string, InsertableTreeNodeFromImplicitAllowedTypes]> : unknown; @@ -1312,8 +1317,10 @@ export type ValidateRecursiveSchema> = true; // @public @sealed -export interface WithType { - get [typeNameSymbol](): TName; +export interface WithType { + // @deprecated + get [typeNameSymbol](): Name; + get [typeSchemaSymbol](): TreeNodeSchemaClass; } ``` diff --git a/packages/framework/fluid-framework/api-report/fluid-framework.legacy.public.api.md b/packages/framework/fluid-framework/api-report/fluid-framework.legacy.public.api.md index f87b335ffce7..87c2d83967c8 100644 --- a/packages/framework/fluid-framework/api-report/fluid-framework.legacy.public.api.md +++ b/packages/framework/fluid-framework/api-report/fluid-framework.legacy.public.api.md @@ -708,17 +708,17 @@ export interface SchemaCompatibilityStatus { // @public @sealed export class SchemaFactory { constructor(scope: TScope); - array(allowedTypes: T): TreeNodeSchema`>, NodeKind.Array, TreeArrayNode & WithType`>>, Iterable>, true, T>; - array(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Array, TreeArrayNode & WithType>, Iterable>, true, T>; - arrayRecursive>(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Array, TreeArrayNodeUnsafe & WithType>, { + array(allowedTypes: T): TreeNodeSchema`>, NodeKind.Array, TreeArrayNode & WithType`>, NodeKind.Array>, Iterable>, true, T>; + array(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Array, TreeArrayNode & WithType, NodeKind.Array>, Iterable>, true, T>; + arrayRecursive>(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Array, TreeArrayNodeUnsafe & WithType, NodeKind.Array>, { [Symbol.iterator](): Iterator>; }, false, T>; readonly boolean: TreeNodeSchema<"com.fluidframework.leaf.boolean", NodeKind.Leaf, boolean, boolean>; readonly handle: TreeNodeSchema<"com.fluidframework.leaf.handle", NodeKind.Leaf, IFluidHandle, IFluidHandle>; get identifier(): FieldSchema; - map(allowedTypes: T): TreeNodeSchema`>, NodeKind.Map, TreeMapNode & WithType`>>, MapNodeInsertableData, true, T>; - map(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Map, TreeMapNode & WithType>, MapNodeInsertableData, true, T>; - mapRecursive>(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Map, TreeMapNodeUnsafe & WithType>, { + map(allowedTypes: T): TreeNodeSchema`>, NodeKind.Map, TreeMapNode & WithType`>, NodeKind.Map>, MapNodeInsertableData, true, T>; + map(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Map, TreeMapNode & WithType, NodeKind.Map>, MapNodeInsertableData, true, T>; + mapRecursive>(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Map, TreeMapNodeUnsafe & WithType, NodeKind.Map>, { [Symbol.iterator](): Iterator<[ string, InsertableTreeNodeFromImplicitAllowedTypesUnsafe @@ -848,7 +848,9 @@ export interface TreeMapNodeUnsafe> e export abstract class TreeNode implements WithType { static [Symbol.hasInstance](value: unknown): value is TreeNode; static [Symbol.hasInstance] TreeNode>(this: TSchema, value: unknown): value is InstanceType; + // @deprecated abstract get [typeNameSymbol](): string; + abstract get [typeSchemaSymbol](): TreeNodeSchemaClass; protected constructor(token: unknown); } @@ -894,10 +896,10 @@ interface TreeNodeSchemaNonClass, TypeName extends string = string> = TreeNode & ObjectFromSchemaRecord & WithType; +export type TreeObjectNode, TypeName extends string = string> = TreeNode & ObjectFromSchemaRecord & WithType; // @public -export type TreeObjectNodeUnsafe>, TypeName extends string = string> = TreeNode & ObjectFromSchemaRecordUnsafe & WithType; +export type TreeObjectNodeUnsafe>, TypeName extends string = string> = TreeNode & ObjectFromSchemaRecordUnsafe & WithType; // @public export enum TreeStatus { @@ -934,9 +936,12 @@ export interface TreeViewEvents { schemaChanged(): void; } -// @public +// @public @deprecated const typeNameSymbol: unique symbol; +// @public +export const typeSchemaSymbol: unique symbol; + // @public export type Unenforced<_DesiredExtendsConstraint> = unknown; @@ -944,7 +949,7 @@ export type Unenforced<_DesiredExtendsConstraint> = unknown; export type Unhydrated = T; // @public -export type ValidateRecursiveSchema, { +export type ValidateRecursiveSchema, { [NodeKind.Object]: T["info"] extends RestrictiveReadonlyRecord ? InsertableObjectFromSchemaRecord : unknown; [NodeKind.Array]: T["info"] extends ImplicitAllowedTypes ? Iterable> : unknown; [NodeKind.Map]: T["info"] extends ImplicitAllowedTypes ? Iterable<[string, InsertableTreeNodeFromImplicitAllowedTypes]> : unknown; @@ -955,8 +960,10 @@ export type ValidateRecursiveSchema> = true; // @public @sealed -export interface WithType { - get [typeNameSymbol](): TName; +export interface WithType { + // @deprecated + get [typeNameSymbol](): Name; + get [typeSchemaSymbol](): TreeNodeSchemaClass; } ``` diff --git a/packages/framework/fluid-framework/api-report/fluid-framework.public.api.md b/packages/framework/fluid-framework/api-report/fluid-framework.public.api.md index 8a4e0cdb7161..8ca5b04796f6 100644 --- a/packages/framework/fluid-framework/api-report/fluid-framework.public.api.md +++ b/packages/framework/fluid-framework/api-report/fluid-framework.public.api.md @@ -672,17 +672,17 @@ export interface SchemaCompatibilityStatus { // @public @sealed export class SchemaFactory { constructor(scope: TScope); - array(allowedTypes: T): TreeNodeSchema`>, NodeKind.Array, TreeArrayNode & WithType`>>, Iterable>, true, T>; - array(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Array, TreeArrayNode & WithType>, Iterable>, true, T>; - arrayRecursive>(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Array, TreeArrayNodeUnsafe & WithType>, { + array(allowedTypes: T): TreeNodeSchema`>, NodeKind.Array, TreeArrayNode & WithType`>, NodeKind.Array>, Iterable>, true, T>; + array(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Array, TreeArrayNode & WithType, NodeKind.Array>, Iterable>, true, T>; + arrayRecursive>(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Array, TreeArrayNodeUnsafe & WithType, NodeKind.Array>, { [Symbol.iterator](): Iterator>; }, false, T>; readonly boolean: TreeNodeSchema<"com.fluidframework.leaf.boolean", NodeKind.Leaf, boolean, boolean>; readonly handle: TreeNodeSchema<"com.fluidframework.leaf.handle", NodeKind.Leaf, IFluidHandle, IFluidHandle>; get identifier(): FieldSchema; - map(allowedTypes: T): TreeNodeSchema`>, NodeKind.Map, TreeMapNode & WithType`>>, MapNodeInsertableData, true, T>; - map(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Map, TreeMapNode & WithType>, MapNodeInsertableData, true, T>; - mapRecursive>(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Map, TreeMapNodeUnsafe & WithType>, { + map(allowedTypes: T): TreeNodeSchema`>, NodeKind.Map, TreeMapNode & WithType`>, NodeKind.Map>, MapNodeInsertableData, true, T>; + map(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Map, TreeMapNode & WithType, NodeKind.Map>, MapNodeInsertableData, true, T>; + mapRecursive>(name: Name, allowedTypes: T): TreeNodeSchemaClass, NodeKind.Map, TreeMapNodeUnsafe & WithType, NodeKind.Map>, { [Symbol.iterator](): Iterator<[ string, InsertableTreeNodeFromImplicitAllowedTypesUnsafe @@ -808,7 +808,9 @@ export interface TreeMapNodeUnsafe> e export abstract class TreeNode implements WithType { static [Symbol.hasInstance](value: unknown): value is TreeNode; static [Symbol.hasInstance] TreeNode>(this: TSchema, value: unknown): value is InstanceType; + // @deprecated abstract get [typeNameSymbol](): string; + abstract get [typeSchemaSymbol](): TreeNodeSchemaClass; protected constructor(token: unknown); } @@ -854,10 +856,10 @@ interface TreeNodeSchemaNonClass, TypeName extends string = string> = TreeNode & ObjectFromSchemaRecord & WithType; +export type TreeObjectNode, TypeName extends string = string> = TreeNode & ObjectFromSchemaRecord & WithType; // @public -export type TreeObjectNodeUnsafe>, TypeName extends string = string> = TreeNode & ObjectFromSchemaRecordUnsafe & WithType; +export type TreeObjectNodeUnsafe>, TypeName extends string = string> = TreeNode & ObjectFromSchemaRecordUnsafe & WithType; // @public export enum TreeStatus { @@ -894,9 +896,12 @@ export interface TreeViewEvents { schemaChanged(): void; } -// @public +// @public @deprecated const typeNameSymbol: unique symbol; +// @public +export const typeSchemaSymbol: unique symbol; + // @public export type Unenforced<_DesiredExtendsConstraint> = unknown; @@ -904,7 +909,7 @@ export type Unenforced<_DesiredExtendsConstraint> = unknown; export type Unhydrated = T; // @public -export type ValidateRecursiveSchema, { +export type ValidateRecursiveSchema, { [NodeKind.Object]: T["info"] extends RestrictiveReadonlyRecord ? InsertableObjectFromSchemaRecord : unknown; [NodeKind.Array]: T["info"] extends ImplicitAllowedTypes ? Iterable> : unknown; [NodeKind.Map]: T["info"] extends ImplicitAllowedTypes ? Iterable<[string, InsertableTreeNodeFromImplicitAllowedTypes]> : unknown; @@ -915,8 +920,10 @@ export type ValidateRecursiveSchema> = true; // @public @sealed -export interface WithType { - get [typeNameSymbol](): TName; +export interface WithType { + // @deprecated + get [typeNameSymbol](): Name; + get [typeSchemaSymbol](): TreeNodeSchemaClass; } ``` From 629bb2ab439a7eb3ea69bfb8149ac92f4db98328 Mon Sep 17 00:00:00 2001 From: "Craig Macomber (Microsoft)" <42876482+CraigMacomber@users.noreply.github.com> Date: Thu, 15 Aug 2024 15:16:51 -0700 Subject: [PATCH 2/5] Update packages/dds/tree/src/simple-tree/core/withType.ts Co-authored-by: Joshua Smithrud <54606601+Josmithr@users.noreply.github.com> --- packages/dds/tree/src/simple-tree/core/withType.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dds/tree/src/simple-tree/core/withType.ts b/packages/dds/tree/src/simple-tree/core/withType.ts index 0dda6d58289a..9fd83cff390a 100644 --- a/packages/dds/tree/src/simple-tree/core/withType.ts +++ b/packages/dds/tree/src/simple-tree/core/withType.ts @@ -31,7 +31,7 @@ export const typeNameSymbol: unique symbol = Symbol("TreeNode Type"); * This symbol should not manually be added to objects as doing so allows the object to be invalidly used where nodes are expected. * Instead construct a real node of the desired type using its constructor. * @privateRemarks - * This prevents non-nodes from being accidentally used as nodes, as well as allows the type checker to distinguish different node types. + * This prevents non-nodes from being accidentally used as nodes and allows the type-checker to distinguish different node types. * @system @public */ export const typeSchemaSymbol: unique symbol = Symbol("TreeNode Schema"); From 1ba7c27e3392eaf4ecd04752f0f8d947b0ad1e01 Mon Sep 17 00:00:00 2001 From: "Craig Macomber (Microsoft)" <42876482+CraigMacomber@users.noreply.github.com> Date: Thu, 15 Aug 2024 15:26:39 -0700 Subject: [PATCH 3/5] Better docs --- .../dds/tree/api-report/tree.alpha.api.md | 6 +-- packages/dds/tree/api-report/tree.beta.api.md | 6 +-- .../dds/tree/api-report/tree.public.api.md | 6 +-- .../dds/tree/src/simple-tree/core/withType.ts | 41 +++++++++++++++---- .../api-report/fluid-framework.beta.api.md | 6 +-- .../fluid-framework.legacy.alpha.api.md | 6 +-- .../fluid-framework.legacy.public.api.md | 6 +-- .../api-report/fluid-framework.public.api.md | 6 +-- 8 files changed, 55 insertions(+), 28 deletions(-) diff --git a/packages/dds/tree/api-report/tree.alpha.api.md b/packages/dds/tree/api-report/tree.alpha.api.md index e6e6fe2ec539..880e71cfbcbf 100644 --- a/packages/dds/tree/api-report/tree.alpha.api.md +++ b/packages/dds/tree/api-report/tree.alpha.api.md @@ -616,10 +616,10 @@ export type ValidateRecursiveSchema> = true; // @public @sealed -export interface WithType { +export interface WithType { // @deprecated - get [typeNameSymbol](): Name; - get [typeSchemaSymbol](): TreeNodeSchemaClass; + get [typeNameSymbol](): TName; + get [typeSchemaSymbol](): TreeNodeSchemaClass; } // (No @packageDocumentation comment for this package) diff --git a/packages/dds/tree/api-report/tree.beta.api.md b/packages/dds/tree/api-report/tree.beta.api.md index e62870775fd4..2c400740fcf5 100644 --- a/packages/dds/tree/api-report/tree.beta.api.md +++ b/packages/dds/tree/api-report/tree.beta.api.md @@ -548,10 +548,10 @@ export type ValidateRecursiveSchema> = true; // @public @sealed -export interface WithType { +export interface WithType { // @deprecated - get [typeNameSymbol](): Name; - get [typeSchemaSymbol](): TreeNodeSchemaClass; + get [typeNameSymbol](): TName; + get [typeSchemaSymbol](): TreeNodeSchemaClass; } // (No @packageDocumentation comment for this package) diff --git a/packages/dds/tree/api-report/tree.public.api.md b/packages/dds/tree/api-report/tree.public.api.md index 977bbfa4f8e2..f6b1d73a3726 100644 --- a/packages/dds/tree/api-report/tree.public.api.md +++ b/packages/dds/tree/api-report/tree.public.api.md @@ -548,10 +548,10 @@ export type ValidateRecursiveSchema> = true; // @public @sealed -export interface WithType { +export interface WithType { // @deprecated - get [typeNameSymbol](): Name; - get [typeSchemaSymbol](): TreeNodeSchemaClass; + get [typeNameSymbol](): TName; + get [typeSchemaSymbol](): TreeNodeSchemaClass; } // (No @packageDocumentation comment for this package) diff --git a/packages/dds/tree/src/simple-tree/core/withType.ts b/packages/dds/tree/src/simple-tree/core/withType.ts index 9fd83cff390a..f68fd3647a04 100644 --- a/packages/dds/tree/src/simple-tree/core/withType.ts +++ b/packages/dds/tree/src/simple-tree/core/withType.ts @@ -16,7 +16,7 @@ import type { NodeKind, TreeNodeSchemaClass } from "./treeNodeSchema.js"; * Instead construct a real node of the desired type using its constructor. * @privateRemarks * This prevents non-nodes from being accidentally used as nodes, as well as allows the type checker to distinguish different node types. - * @deprecated External code should use `Tree.schema(theNode)` for runtime data access, and for typechecking and internally {@link typeSchemaSymbol} provides a superset of this functionality. + * @deprecated External code should use `Tree.schema(theNode)` for schema related runtime data access. For type narrowing, use `WithType` instead of the symbols directly. * @system @public */ export const typeNameSymbol: unique symbol = Symbol("TreeNode Type"); @@ -28,8 +28,10 @@ export const typeNameSymbol: unique symbol = Symbol("TreeNode Type"); * This symbol mainly exists on nodes to allow TypeScript to provide more accurate type checking. * `Tree.is` and `Tree.schema` provide a superset of this information in more friendly ways. * - * This symbol should not manually be added to objects as doing so allows the object to be invalidly used where nodes are expected. + * This symbol should not manually be added to objects as doing so allows the object to be invalidly used where specific nodes are expected. * Instead construct a real node of the desired type using its constructor. + * + * This symbol should not be used directly for type narrowing. Instead use {@link WithType}. * @privateRemarks * This prevents non-nodes from being accidentally used as nodes and allows the type-checker to distinguish different node types. * @system @public @@ -38,22 +40,47 @@ export const typeSchemaSymbol: unique symbol = Symbol("TreeNode Schema"); /** * Adds a type symbol to a type for stronger typing. + * + * @typeParam TName - Same as {@link TreeNodeSchema}'s "Name" parameter. + * @typeParam TKind - Same as {@link TreeNodeSchema}'s "Kind" parameter. * @remarks - * An implementation detail of {@link TreeNode}'s strong typing setup: not intended for direct use outside of this package. + * Powers {@link TreeNode}'s strong typing setup. + * @example Narrow types for overloading based on NodeKind + * ```typescript + * function getKeys(node: TreeNode & WithType): number[]; + * function getKeys(node: TreeNode & WithType): string[]; + * function getKeys(node: TreeNode): string[] | number[]; + * function getKeys(node: TreeNode): string[] | number[] { + * const schema = Tree.schema(node); + * if (schema.kind === NodeKind.Array) { + * const arrayNode = node as TreeArrayNode; + * const keys: number[] = []; + * for (let index = 0; index < arrayNode.length; index++) { + * keys.push(index); + * } + * return keys; + * } + * if (schema.kind === NodeKind.Map) { + * return [...(node as TreeMapNode).keys()]; + * } + * + * return Object.keys(node); + * } + * ``` * @sealed @public */ export interface WithType< - out Name extends string = string, - out Kind extends NodeKind = NodeKind, + out TName extends string = string, + out TKind extends NodeKind = NodeKind, > { /** * Type symbol, marking a type in a way to increase type safety via strong type checking. * @deprecated Use {@link typeSchemaSymbol} instead. */ - get [typeNameSymbol](): Name; + get [typeNameSymbol](): TName; /** * Type symbol, marking a type in a way to increase type safety via strong type checking. */ - get [typeSchemaSymbol](): TreeNodeSchemaClass; + get [typeSchemaSymbol](): TreeNodeSchemaClass; } 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 c7460162f47c..b0dca17e2a9a 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 @@ -920,10 +920,10 @@ export type ValidateRecursiveSchema> = true; // @public @sealed -export interface WithType { +export interface WithType { // @deprecated - get [typeNameSymbol](): Name; - get [typeSchemaSymbol](): TreeNodeSchemaClass; + get [typeNameSymbol](): TName; + get [typeSchemaSymbol](): TreeNodeSchemaClass; } ``` diff --git a/packages/framework/fluid-framework/api-report/fluid-framework.legacy.alpha.api.md b/packages/framework/fluid-framework/api-report/fluid-framework.legacy.alpha.api.md index d6f9b977cb0b..21dc371ef5f3 100644 --- a/packages/framework/fluid-framework/api-report/fluid-framework.legacy.alpha.api.md +++ b/packages/framework/fluid-framework/api-report/fluid-framework.legacy.alpha.api.md @@ -1317,10 +1317,10 @@ export type ValidateRecursiveSchema> = true; // @public @sealed -export interface WithType { +export interface WithType { // @deprecated - get [typeNameSymbol](): Name; - get [typeSchemaSymbol](): TreeNodeSchemaClass; + get [typeNameSymbol](): TName; + get [typeSchemaSymbol](): TreeNodeSchemaClass; } ``` diff --git a/packages/framework/fluid-framework/api-report/fluid-framework.legacy.public.api.md b/packages/framework/fluid-framework/api-report/fluid-framework.legacy.public.api.md index 87c2d83967c8..2367f9d20564 100644 --- a/packages/framework/fluid-framework/api-report/fluid-framework.legacy.public.api.md +++ b/packages/framework/fluid-framework/api-report/fluid-framework.legacy.public.api.md @@ -960,10 +960,10 @@ export type ValidateRecursiveSchema> = true; // @public @sealed -export interface WithType { +export interface WithType { // @deprecated - get [typeNameSymbol](): Name; - get [typeSchemaSymbol](): TreeNodeSchemaClass; + get [typeNameSymbol](): TName; + get [typeSchemaSymbol](): TreeNodeSchemaClass; } ``` diff --git a/packages/framework/fluid-framework/api-report/fluid-framework.public.api.md b/packages/framework/fluid-framework/api-report/fluid-framework.public.api.md index 8ca5b04796f6..9512e8b5921e 100644 --- a/packages/framework/fluid-framework/api-report/fluid-framework.public.api.md +++ b/packages/framework/fluid-framework/api-report/fluid-framework.public.api.md @@ -920,10 +920,10 @@ export type ValidateRecursiveSchema> = true; // @public @sealed -export interface WithType { +export interface WithType { // @deprecated - get [typeNameSymbol](): Name; - get [typeSchemaSymbol](): TreeNodeSchemaClass; + get [typeNameSymbol](): TName; + get [typeSchemaSymbol](): TreeNodeSchemaClass; } ``` From abd7f509f69e5db68cd84ce088a399a8f85549b0 Mon Sep 17 00:00:00 2001 From: "Craig Macomber (Microsoft)" <42876482+CraigMacomber@users.noreply.github.com> Date: Thu, 15 Aug 2024 21:50:49 -0700 Subject: [PATCH 4/5] Update example --- .changeset/fruity-birds-love.md | 25 +++++++++++-------- .../dds/tree/src/simple-tree/core/withType.ts | 25 +++++++++++-------- .../simple-tree/api/schemaFactory.spec.ts | 25 +++++++++++-------- 3 files changed, 42 insertions(+), 33 deletions(-) diff --git a/.changeset/fruity-birds-love.md b/.changeset/fruity-birds-love.md index 68c31cb74ade..9d25e5867b32 100644 --- a/.changeset/fruity-birds-love.md +++ b/.changeset/fruity-birds-love.md @@ -17,18 +17,21 @@ function getKeys(node: TreeNode & WithType Date: Thu, 15 Aug 2024 21:57:18 -0700 Subject: [PATCH 5/5] Fix index export commented sections --- packages/dds/tree/src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/dds/tree/src/index.ts b/packages/dds/tree/src/index.ts index 284a72884ece..074ad528925e 100644 --- a/packages/dds/tree/src/index.ts +++ b/packages/dds/tree/src/index.ts @@ -110,7 +110,7 @@ export { type InsertableTreeFieldFromImplicitFieldUnsafe, type FieldSchemaUnsafe, // System types (not in Internal types for various reasons, like doc links or cannot be named errors). - getJsonSchema, + type typeSchemaSymbol, // Recursive Schema APIs type ValidateRecursiveSchema, type FixRecursiveArraySchema, @@ -138,7 +138,7 @@ export { type JsonRefPath, type JsonSchemaType, type JsonLeafSchemaType, - type typeSchemaSymbol, + getJsonSchema, } from "./simple-tree/index.js"; export { SharedTree, configuredSharedTree } from "./treeFactory.js";