From 9bfc72b63db4fa7c226125a34f8b2b3d82012122 Mon Sep 17 00:00:00 2001 From: sinclair Date: Wed, 29 Nov 2023 03:00:40 +0900 Subject: [PATCH] Revision 0.32.0 --- examples/index.ts | 56 +++++------- hammer.mjs | 1 + src/index.ts | 1 + src/type/builder/json.ts | 5 ++ src/type/builder/type.ts | 1 + src/type/deref/deref.ts | 190 +++++++++++++++++++++++++++++++++++++++ src/type/deref/index.ts | 29 ++++++ src/type/index.ts | 1 + todo.md | 3 +- 9 files changed, 252 insertions(+), 35 deletions(-) create mode 100644 src/type/deref/deref.ts create mode 100644 src/type/deref/index.ts diff --git a/examples/index.ts b/examples/index.ts index ed52156bd..c1af8ddaa 100644 --- a/examples/index.ts +++ b/examples/index.ts @@ -1,40 +1,28 @@ import { TypeSystem } from '@sinclair/typebox/system' import { TypeCompiler } from '@sinclair/typebox/compiler' import { Value, ValuePointer } from '@sinclair/typebox/value' -import { Type, TypeGuard, Kind, Static, TSchema } from '@sinclair/typebox' - -// ----------------------------------------------------------- -// Create: Type -// ----------------------------------------------------------- - -const T = Type.Object({ - x: Type.Number(), - y: Type.Number(), - z: Type.Number(), +import { Type, TypeGuard, Kind, Static, TSchema, TTuple, TIntersect, TUnion, TPromise, TAsyncIterator, TIterator, TArray, TConstructor, TFunction, Clone, TRef, TObject, TProperties } from '@sinclair/typebox' +import { CloneType } from '@sinclair/typebox' +import { ValueGuard } from '@sinclair/typebox' + +const T = Type.Object( + { + x: Type.Number(), + y: Type.String(), + }, + { $id: 'T' }, +) + +const R1 = Type.Ref('T', { $id: 'R1' }) +const R2 = Type.Ref('R1', { $id: 'R2' }) +const R3 = Type.Ref('R2', { $id: 'R3' }) +const R4 = Type.Ref('R3', { $id: 'R4' }) + +const S = Type.Object({ + a: Type.Ref('R4'), + b: Type.Ref('R4'), }) -type T = Static - -console.log(T) - -// ----------------------------------------------------------- -// Create: Value -// ----------------------------------------------------------- - -const V = Value.Create(T) - -console.log(V) - -// ----------------------------------------------------------- -// Compile: Type -// ----------------------------------------------------------- - -const C = TypeCompiler.Compile(T) - -console.log(C.Code()) - -// ----------------------------------------------------------- -// Check: Value -// ----------------------------------------------------------- +const X = Type.Deref(S, [T, R1, R2, R3, R4]) -console.log(C.Check(V)) +console.log(X) diff --git a/hammer.mjs b/hammer.mjs index 8e8a985ee..ace197967 100644 --- a/hammer.mjs +++ b/hammer.mjs @@ -5,6 +5,7 @@ import { readFileSync } from 'fs' // Clean // ------------------------------------------------------------------------------- export async function clean() { + await folder('node_modules/typebox').delete() await folder('target').delete() } // ------------------------------------------------------------------------------- diff --git a/src/index.ts b/src/index.ts index 33b313bc1..09ab949c4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -49,6 +49,7 @@ export { Const, type TConst } from './type/const/index' export { Constructor, type TConstructor } from './type/constructor/index' export { ConstructorParameters, type TConstructorParameters } from './type/constructor-parameters/index' export { Date, type TDate, type DateOptions } from './type/date/index' +export { Deref, type TDeref } from './type/deref/index' export { Enum, type TEnum } from './type/enum/index' export { Exclude, type TExclude } from './type/exclude/index' export { Extends, type TExtends } from './type/extends/index' diff --git a/src/type/builder/json.ts b/src/type/builder/json.ts index 15c81cfcb..44bd04ad2 100644 --- a/src/type/builder/json.ts +++ b/src/type/builder/json.ts @@ -30,6 +30,7 @@ import { Any, type TAny } from '../any/index' import { Array, type TArray, type ArrayOptions } from '../array/index' import { Boolean, type TBoolean } from '../boolean/index' import { Composite, type TComposite } from '../composite/index' +import { Deref, type TDeref } from '../deref/index' import { Enum, type TEnum, type TEnumKey, type TEnumValue } from '../enum/index' import { Exclude, type TExclude } from '../exclude/index' import { Extends, type TExtends } from '../extends/index' @@ -113,6 +114,10 @@ export class JsonTypeBuilder { public Composite(objects: [...T], options?: ObjectOptions): TComposite { return Composite(objects, options) } + /** [Json] Dereferences a schema to its target type */ + public Deref(schema: T, references: TSchema[]): TDeref { + return Deref(schema, references) + } /** `[Json]` Creates a Enum type */ public Enum>(item: T, options: SchemaOptions = {}): TEnum { return Enum(item, options) diff --git a/src/type/builder/type.ts b/src/type/builder/type.ts index 4671ededb..3abef6cf3 100644 --- a/src/type/builder/type.ts +++ b/src/type/builder/type.ts @@ -40,6 +40,7 @@ export { Const } from '../const/index' export { Constructor } from '../constructor/index' export { ConstructorParameters } from '../constructor-parameters/index' export { Date } from '../date/index' +export { Deref } from '../deref/index' export { Enum } from '../enum/index' export { Exclude } from '../exclude/index' export { Extends } from '../extends/index' diff --git a/src/type/deref/deref.ts b/src/type/deref/deref.ts new file mode 100644 index 000000000..822121f20 --- /dev/null +++ b/src/type/deref/deref.ts @@ -0,0 +1,190 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema } from '../schema/index' +import type { Evaluate } from '../helpers/index' +import type { TTuple } from '../tuple/index' +import type { TIntersect } from '../intersect/index' +import type { TUnion } from '../union/index' +import type { TPromise } from '../promise/index' +import type { TAsyncIterator } from '../async-iterator/index' +import type { TIterator } from '../iterator/index' +import type { TArray } from '../array/index' +import type { TConstructor } from '../constructor/index' +import type { TFunction } from '../function/index' +import type { TRef } from '../ref/index' +import type { TObject, TProperties } from '../object/index' +import { CloneType } from '../clone/type' +import { IsUndefined } from '../guard/value' + +import { + TConstructor as IsConstructorType, + TFunction as IsFunctionType, + TIntersect as IsIntersectType, + TUnion as IsUnionType, + TTuple as IsTupleType, + TArray as IsArrayType, + TObject as IsObjectType, + TPromise as IsPromiseType, + TAsyncIterator as IsAsyncIteratorType, + TIterator as IsIteratorType, + TRef as IsRefType, +} from '../guard/type' + +// ------------------------------------------------------------------ +// DerefResolve +// ------------------------------------------------------------------ +// prettier-ignore +export type FromRest = ( + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? [DerefResolve, ...FromRest] + : [] +) +function FromRest(schema: TSchema[], references: TSchema[]) { + return schema.map((schema) => Deref(schema, references)) +} +// prettier-ignore +type FromProperties = Evaluate<{ + [K in keyof T]: DerefResolve +}> +// prettier-ignore +function FromProperties(properties: TProperties, references: TSchema[]) { + return globalThis.Object.getOwnPropertyNames(properties).reduce((acc, key) => { + return {...acc, [key]: Deref(properties[key], references) } + }, {} as TProperties) +} +// prettier-ignore +function FromConstructor(schema: TConstructor, references: TSchema[]) { + const clone = CloneType(schema) + clone.parameters = FromRest(clone.parameters, references) + clone.returns = Deref(clone.returns, references) + return clone +} +// prettier-ignore +function FromFunction(schema: TFunction, references: TSchema[]) { + const clone = CloneType(schema) + clone.parameters = FromRest(clone.parameters, references) + clone.returns = Deref(clone.returns, references) + return clone +} +// prettier-ignore +function FromIntersect(schema: TIntersect, references: TSchema[]) { + const clone = CloneType(schema) + clone.allOf = FromRest(clone.allOf, references) + return clone +} +// prettier-ignore +function FromUnion(schema: TUnion, references: TSchema[]) { + const clone = CloneType(schema) + clone.anyOf = FromRest(clone.anyOf, references) + return clone +} +// prettier-ignore +function FromTuple(schema: TTuple, references: TSchema[]) { + const clone = CloneType(schema) + if(IsUndefined(clone.items)) return clone + clone.items = FromRest(clone.items, references) + return clone +} +// prettier-ignore +function FromArray(schema: TArray, references: TSchema[]) { + const clone = CloneType(schema) + clone.items = Deref(clone.items, references) + return clone +} +// prettier-ignore +function FromObject(schema: TObject, references: TSchema[]) { + const clone = CloneType(schema) + clone.properties = FromProperties(clone.properties, references) + return clone +} +// prettier-ignore +function FromPromise(schema: TPromise, references: TSchema[]) { + const clone = CloneType(schema) + clone.item = Deref(clone.item, references) + return clone +} +// prettier-ignore +function FromAsyncIterator(schema: TAsyncIterator, references: TSchema[]) { + const clone = CloneType(schema) + clone.items = Deref(clone.items, references) + return clone +} +// prettier-ignore +function FromIterator(schema: TIterator, references: TSchema[]) { + const clone = CloneType(schema) + clone.items = Deref(clone.items, references) + return clone +} +// prettier-ignore +function FromRef(schema: TRef, references: TSchema[]) { + const target = references.find(remote => remote.$id === schema.$ref) + if(target === undefined) throw Error(`Unable to dereference schema with $id ${schema.$ref}`) + return Deref(target, references) +} +// prettier-ignore +export type DerefResolve = + T extends TConstructor ? TConstructor, DerefResolve> : + T extends TFunction ? TFunction, DerefResolve> : + T extends TIntersect ? TIntersect> : + T extends TUnion ? TUnion> : + T extends TTuple ? TTuple> : + T extends TObject ? TObject> : + T extends TArray ? TArray> : + T extends TPromise ? TPromise> : + T extends TAsyncIterator ? TAsyncIterator> : + T extends TIterator ? TIterator> : + T extends TRef ? DerefResolve : + T +// prettier-ignore +export function DerefResolve(schema: T, references: TSchema[]): TDeref { + return ( + IsConstructorType(schema) ? FromConstructor(schema, references) : + IsFunctionType(schema) ? FromFunction(schema, references) : + IsIntersectType(schema) ? FromIntersect(schema, references) : + IsUnionType(schema) ? FromUnion(schema, references) : + IsTupleType(schema) ? FromTuple(schema, references) : + IsArrayType(schema) ? FromArray(schema, references) : + IsObjectType(schema) ? FromObject(schema, references) : + IsPromiseType(schema) ? FromPromise(schema, references) : + IsAsyncIteratorType(schema) ? FromAsyncIterator(schema, references) : + IsIteratorType(schema) ? FromIterator(schema, references) : + IsRefType(schema) ? FromRef(schema, references) : + schema + ) as TDeref +} +// ------------------------------------------------------------------ +// TDeref +// ------------------------------------------------------------------ +export type TDeref = DerefResolve + +/** [Json] Dereferences a schema to its target type */ +// prettier-ignore +export function Deref(schema: T, references: TSchema[]): TDeref { + return DerefResolve(schema, references) +} diff --git a/src/type/deref/index.ts b/src/type/deref/index.ts new file mode 100644 index 000000000..fdc0b4858 --- /dev/null +++ b/src/type/deref/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './deref' diff --git a/src/type/index.ts b/src/type/index.ts index 09dcd54be..bfdb0f989 100644 --- a/src/type/index.ts +++ b/src/type/index.ts @@ -39,6 +39,7 @@ export * from './const/index' export * from './constructor/index' export * from './constructor-parameters/index' export * from './date/index' +export * from './deref/index' export * from './discard/index' export * from './enum/index' export * from './exclude/index' diff --git a/todo.md b/todo.md index 03ebd5de6..d7401d2f2 100644 --- a/todo.md +++ b/todo.md @@ -5,6 +5,7 @@ - [x] Exclude, Extract and Extends could use a clean up - [x] Look for invalid import references - [x] The Symbols Change May be a bit too breaking. Move Kind, Hint, and others to the top (again) -- [ ] Implement static tests for Type.Const() +- [ ] Implement tests for Type.Const() +- [ ] Implement tests for Type.Deref() - [ ] Implement experimental Type.Mapped(). This will only be usable on the Workbench - [ ] Maybe rename the arguments back to 'schema' (T is a little verbose) \ No newline at end of file