From 85962827d7f86750d29d48c665b3fda3c9385bff Mon Sep 17 00:00:00 2001 From: sinclair Date: Thu, 14 Nov 2024 16:58:20 +0900 Subject: [PATCH] Implement Module Deref Types --- example/index.ts | 83 ++--- src/compiler/compiler.ts | 2 +- src/errors/errors.ts | 2 +- src/syntax/static.ts | 8 +- src/type/awaited/awaited.ts | 94 +++--- src/type/computed/computed.ts | 44 +++ src/type/computed/index.ts | 29 ++ src/type/guard/kind.ts | 17 +- src/type/guard/type.ts | 12 +- src/type/keyof/index.ts | 1 + src/type/keyof/keyof-from-mapped-result.ts | 42 +-- src/type/keyof/keyof-ref.ts | 41 +++ src/type/keyof/keyof.ts | 57 ++-- src/type/module/compute.ts | 345 +++++++++++++++++++++ src/type/module/infer.ts | 162 ++++++++++ src/type/module/module.ts | 152 ++------- src/type/omit/omit-from-mapped-key.ts | 73 ++--- src/type/omit/omit-from-mapped-result.ts | 52 ++-- src/type/omit/omit.ts | 163 ++++++---- src/type/partial/partial.ts | 89 ++++-- src/type/pick/pick-from-mapped-key.ts | 71 ++--- src/type/pick/pick-from-mapped-result.ts | 52 ++-- src/type/pick/pick.ts | 160 ++++++---- src/type/required/index.ts | 1 + src/type/required/required-ref.ts | 41 +++ src/type/required/required.ts | 47 +-- src/type/type/json.ts | 42 +-- src/value/cast/cast.ts | 2 +- src/value/check/check.ts | 6 +- src/value/clean/clean.ts | 2 +- src/value/convert/convert.ts | 2 +- src/value/create/create.ts | 2 +- src/value/default/default.ts | 2 +- src/value/transform/decode.ts | 6 +- src/value/transform/encode.ts | 6 +- src/value/transform/has.ts | 4 +- test/static/pick.ts | 2 +- 37 files changed, 1298 insertions(+), 618 deletions(-) create mode 100644 src/type/computed/computed.ts create mode 100644 src/type/computed/index.ts create mode 100644 src/type/keyof/keyof-ref.ts create mode 100644 src/type/module/compute.ts create mode 100644 src/type/module/infer.ts create mode 100644 src/type/required/required-ref.ts diff --git a/example/index.ts b/example/index.ts index 9d228684a..c871883a9 100644 --- a/example/index.ts +++ b/example/index.ts @@ -4,46 +4,57 @@ import { Value, ValuePointer } from '@sinclair/typebox/value' import { Parse, StaticParseAsType } from '@sinclair/typebox/syntax' import { Type, TypeGuard, Kind, Static, TSchema } from '@sinclair/typebox' -// ----------------------------------------------------------- -// Create: Type -// ----------------------------------------------------------- - -const T = Type.Object({ - x: Type.Number(), - y: Type.Number(), - z: Type.Number(), +// KindGuard.IsLiteralValue + +// New Types: +// - Awaited (Computed) +// - Partial (Computed) +// - Pick (Computed) +// - Omit (Computed) +// - Required +// - KeyOf + +// +// Todo: +// - Have more problems dealing with options not mapping over correctly. This is a +// problem for deferred types as the XRef can't be naively assign options post +// creation. I could check for these cases however. +// - Support Recursive Deref. This will be challenging, but ideally we want to +// support Partial(Required(Partial(Type.Ref('A')))). This probably means +// ditching the current XRef for a Deferred node like thing. + +const M = Parse('string') + +// const Module = Parse(`module { +// type A = { +// x: number +// y: number +// z: number +// } + +// type K = 'z' | 'y' + +// type T = Partial> +// }`) + +const Module = Type.Module({ + A: Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }), + K: Type.Union([Type.Literal('z'), Type.Literal('y')]), + T: Type.Partial(Type.Omit(Type.Ref('A'), Type.Ref('K'))), }) -type T = Static +const T = Module.Import('T') -console.log(T) +function test(value: Static) {} -// ----------------------------------------------------------- -// Parse: Type -// ----------------------------------------------------------- +console.dir(T, { depth: 100 }) -const S = Parse({ T }, `{ x: T, y: T, z: T }`) +type A = Static -type S = Static +// Ok(T, { nodes: [{ nodes: [{ nodes: [] }, { nodes: [] }] }] }) -// ----------------------------------------------------------- -// Create: Value -// ----------------------------------------------------------- - -const V = Value.Create(T) - -console.log(V) - -// ----------------------------------------------------------- -// Compile: Type -// ----------------------------------------------------------- - -const C = TypeCompiler.Compile(T) - -console.log(C.Code()) - -// ----------------------------------------------------------- -// Check: Value -// ----------------------------------------------------------- - -console.log(C.Check(V)) +const A = Type.Omit(Type.String(), Type.Ref('A')) \ No newline at end of file diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 6e438702b..01ab4dcf6 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -291,7 +291,7 @@ export namespace TypeCompiler { } function* FromImport(schema: TImport, references: TSchema[], value: string): IterableIterator { const definitions = globalThis.Object.values(schema.$defs) as TSchema[] - const target = schema.$defs[schema.$ref] as TSchema + const target = schema.$defs[schema.$ref as never] as TSchema yield* Visit(target, [...references, ...definitions], value) } function* FromInteger(schema: TInteger, references: TSchema[], value: string): IterableIterator { diff --git a/src/errors/errors.ts b/src/errors/errors.ts index 9bb1f6cd1..b7ac37f4c 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -305,7 +305,7 @@ function* FromFunction(schema: TFunction, references: TSchema[], path: string, v } function* FromImport(schema: TImport, references: TSchema[], path: string, value: any): IterableIterator { const definitions = globalThis.Object.values(schema.$defs) as TSchema[] - const target = schema.$defs[schema.$ref] as TSchema + const target = schema.$defs[schema.$ref as never] as TSchema yield* Visit(target, [...references, ...definitions], path, value) } function* FromInteger(schema: TInteger, references: TSchema[], path: string, value: any): IterableIterator { diff --git a/src/syntax/static.ts b/src/syntax/static.ts index 0b30b8ead..c8407ae5e 100644 --- a/src/syntax/static.ts +++ b/src/syntax/static.ts @@ -788,8 +788,8 @@ type Required = Static.Tuple<[ // ------------------------------------------------------------------ // prettier-ignore interface PickMapping extends Static.IMapping { - output: this['input'] extends ['Pick', LAngle, infer Type extends Types.TSchema, Comma, infer PropertyKey extends Types.TSchema, RAngle] - ? Types.TPick> + output: this['input'] extends ['Pick', LAngle, infer Type extends Types.TSchema, Comma, infer Key extends Types.TSchema, RAngle] + ? Types.TPick : never } // prettier-ignore @@ -801,8 +801,8 @@ type Pick = Static.Tuple<[ // ------------------------------------------------------------------ // prettier-ignore interface OmitMapping extends Static.IMapping { - output: this['input'] extends ['Omit', LAngle, infer Type extends Types.TSchema, Comma, infer PropertyKey extends Types.TSchema, RAngle] - ? Types.TOmit> + output: this['input'] extends ['Omit', LAngle, infer Type extends Types.TSchema, Comma, infer Key extends Types.TSchema, RAngle] + ? Types.TOmit : never } // prettier-ignore diff --git a/src/type/awaited/awaited.ts b/src/type/awaited/awaited.ts index e129b6a52..1a849f14e 100644 --- a/src/type/awaited/awaited.ts +++ b/src/type/awaited/awaited.ts @@ -26,76 +26,96 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' +import { Ensure } from '../helpers/index' import type { TSchema, SchemaOptions } from '../schema/index' +import { Computed, type TComputed } from '../computed/index' import { Intersect, type TIntersect } from '../intersect/index' import { Union, type TUnion } from '../union/index' import { type TPromise } from '../promise/index' -import { CreateType } from '../create/type' +import { Ref, type TRef } from '../ref/index' // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -import { IsIntersect, IsUnion, IsPromise } from '../guard/kind' -// ------------------------------------------------------------------ -// FromRest -// ------------------------------------------------------------------ +import { IsIntersect, IsUnion, IsPromise, IsRef, IsComputed } from '../guard/kind' + +// ---------------------------------------------------------------- +// FromComputed +// ---------------------------------------------------------------- // prettier-ignore -type TFromRest = - T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? TFromRest]> - : Acc +type TFromComputed = Ensure<( + TComputed<'Awaited', [TComputed]> +)> +// prettier-ignore +function FromComputed(target: Target, parameters: Parameters): TFromComputed { + return Computed('Awaited', [Computed(target, parameters)]) as never +} +// ---------------------------------------------------------------- +// Ref +// ---------------------------------------------------------------- +type TFromRef = Ensure]>> // prettier-ignore -function FromRest(T: [...T]) : TFromRest { - return T.map(L => AwaitedResolve(L)) as never +function FromRef($ref: Ref): TFromRef { + return Computed('Awaited', [Ref($ref)]) as never } // ---------------------------------------------------------------- // FromIntersect // ---------------------------------------------------------------- // prettier-ignore -type TFromIntersect = TIntersect> +type TFromIntersect = ( + TIntersect> +) // prettier-ignore -function FromIntersect(T: [...T]): TFromIntersect { - return Intersect(FromRest(T) as TSchema[]) as never +function FromIntersect(types: [...Types]): TFromIntersect { + return Intersect(FromRest(types) as TSchema[]) as never } // ---------------------------------------------------------------- // FromUnion // ---------------------------------------------------------------- // prettier-ignore -type TFromUnion = TUnion> +type TFromUnion = TUnion> // prettier-ignore -function FromUnion(T: [...T]): TFromUnion { - return Union(FromRest(T) as TSchema[]) as never +function FromUnion(types: [...Types]): TFromUnion { + return Union(FromRest(types) as TSchema[]) as never } // ---------------------------------------------------------------- // Promise // ---------------------------------------------------------------- -type TFromPromise = TAwaited +type TFromPromise = TAwaited // prettier-ignore -function FromPromise(T: T): TFromPromise { - return AwaitedResolve(T) as never +function FromPromise(type: Type): TFromPromise { + return Awaited(type) as never } -// ---------------------------------------------------------------- -// AwaitedResolve -// ---------------------------------------------------------------- +// ------------------------------------------------------------------ +// FromRest +// ------------------------------------------------------------------ // prettier-ignore -function AwaitedResolve(T: T): TAwaited { - return ( - IsIntersect(T) ? FromIntersect(T.allOf) : - IsUnion(T) ? FromUnion(T.anyOf) : - IsPromise(T) ? FromPromise(T.item) : - T - ) as never +type TFromRest = ( + Types extends [infer Left extends TSchema, ...infer Right extends TSchema[]] + ? TFromRest]> + : Result +) +// prettier-ignore +function FromRest(types: [...Types]) : TFromRest { + return types.map(type => Awaited(type)) as never } // ------------------------------------------------------------------ // TAwaited // ------------------------------------------------------------------ // prettier-ignore -export type TAwaited = - T extends TIntersect ? TIntersect> : - T extends TUnion ? TUnion> : - T extends TPromise ? TAwaited : - T +export type TAwaited = ( + Type extends TComputed ? TFromComputed : + Type extends TRef ? TFromRef : + Type extends TIntersect ? TIntersect> : + Type extends TUnion ? TUnion> : + Type extends TPromise ? TAwaited : + Type +) /** `[JavaScript]` Constructs a type by recursively unwrapping Promise types */ -export function Awaited(T: T, options?: SchemaOptions): TAwaited { - return CreateType(AwaitedResolve(T), options) as never +export function Awaited(type: T, options?: SchemaOptions): TAwaited { + return CreateType( + IsComputed(type) ? FromComputed(type.target, type.parameters) : IsIntersect(type) ? FromIntersect(type.allOf) : IsUnion(type) ? FromUnion(type.anyOf) : IsPromise(type) ? FromPromise(type.item) : IsRef(type) ? FromRef(type.$ref) : type, + options, + ) as never } diff --git a/src/type/computed/computed.ts b/src/type/computed/computed.ts new file mode 100644 index 000000000..2524dc152 --- /dev/null +++ b/src/type/computed/computed.ts @@ -0,0 +1,44 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2024 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, SchemaOptions } from '../schema/index' +import { CreateType } from '../create/index' +import { Kind } from '../symbols/symbols' + +// ------------------------------------------------------------------ +// Computed +// ------------------------------------------------------------------ +export interface TComputed extends TSchema { + [Kind]: 'Computed' + target: Target + parameters: Parameters +} +/** `[Internal]` Creates a deferred computed type. This type is used exclusively in modules to defer resolution of computable types that contain interior references */ +export function Computed(target: Target, parameters: [...Parameters], options?: SchemaOptions): TComputed { + return CreateType({ [Kind]: 'Computed', target, parameters }, options) as never +} diff --git a/src/type/computed/index.ts b/src/type/computed/index.ts new file mode 100644 index 000000000..4d20b1f87 --- /dev/null +++ b/src/type/computed/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2024 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 './computed' diff --git a/src/type/guard/kind.ts b/src/type/guard/kind.ts index 2e61a1df9..95e14fc2f 100644 --- a/src/type/guard/kind.ts +++ b/src/type/guard/kind.ts @@ -29,9 +29,10 @@ THE SOFTWARE. import * as ValueGuard from './value' import { Kind, Hint, TransformKind, ReadonlyKind, OptionalKind } from '../symbols/index' import { TransformOptions } from '../transform/index' -import { TTemplateLiteral } from '../template-literal/index' -import { TArray } from '../array/index' -import { TBoolean } from '../boolean/index' +import type { TTemplateLiteral } from '../template-literal/index' +import type { TArray } from '../array/index' +import type { TBoolean } from '../boolean/index' +import type { TComputed } from '../computed/index' import type { TRecord } from '../record/index' import type { TString } from '../string/index' import type { TUnion } from '../union/index' @@ -44,7 +45,7 @@ import type { TImport } from '../module/index' import type { TInteger } from '../integer/index' import type { TIntersect } from '../intersect/index' import type { TIterator } from '../iterator/index' -import type { TLiteral } from '../literal/index' +import type { TLiteral, TLiteralValue } from '../literal/index' import type { TMappedKey, TMappedResult } from '../mapped/index' import type { TNever } from '../never/index' import type { TNot } from '../not/index' @@ -95,6 +96,10 @@ export function IsBigInt(value: unknown): value is TBigInt { export function IsBoolean(value: unknown): value is TBoolean { return IsKindOf(value, 'Boolean') } +/** `[Kind-Only]` Returns true if the given value is TComputed */ +export function IsComputed(value: unknown): value is TComputed { + return IsKindOf(value, 'Computed') +} /** `[Kind-Only]` Returns true if the given value is TConstructor */ export function IsConstructor(value: unknown): value is TConstructor { return IsKindOf(value, 'Constructor') @@ -143,6 +148,10 @@ export function IsLiteralNumber(value: unknown): value is TLiteral { export function IsLiteralBoolean(value: unknown): value is TLiteral { return IsLiteral(value) && ValueGuard.IsBoolean(value.const) } +/** `[Kind-Only]` Returns true if the given value is TLiteralValue */ +export function IsLiteralValue(value: unknown): value is TLiteralValue { + return ValueGuard.IsBoolean(value) || ValueGuard.IsNumber(value) || ValueGuard.IsString(value) +} /** `[Kind-Only]` Returns true if the given value is TLiteral */ export function IsLiteral(value: unknown): value is TLiteral { return IsKindOf(value, 'Literal') diff --git a/src/type/guard/type.ts b/src/type/guard/type.ts index ed8eb54fe..b4564bed2 100644 --- a/src/type/guard/type.ts +++ b/src/type/guard/type.ts @@ -30,9 +30,10 @@ import * as ValueGuard from './value' import { Kind, Hint, TransformKind, ReadonlyKind, OptionalKind } from '../symbols/index' import { TypeBoxError } from '../error/index' import { TransformOptions } from '../transform/index' -import { TTemplateLiteral } from '../template-literal/index' -import { TArray } from '../array/index' -import { TBoolean } from '../boolean/index' +import type { TTemplateLiteral } from '../template-literal/index' +import type { TArray } from '../array/index' +import type { TBoolean } from '../boolean/index' +import type { TComputed } from '../computed/index' import type { TRecord } from '../record/index' import type { TString } from '../string/index' import type { TUnion } from '../union/index' @@ -76,6 +77,7 @@ const KnownTypes = [ 'AsyncIterator', 'BigInt', 'Boolean', + 'Computed', 'Constructor', 'Date', 'Enum', @@ -217,6 +219,10 @@ export function IsBoolean(value: unknown): value is TBoolean { IsOptionalString(value.$id) ) } +/** Returns true if the given value is TComputed */ +export function IsComputed(value: unknown): value is TComputed { + return IsKindOf(value, 'Computed') && IsString(value.target) && ValueGuard.IsArray(value.parameters) && value.parameters.every((schema) => IsSchema(schema)) +} /** Returns true if the given value is TConstructor */ export function IsConstructor(value: unknown): value is TConstructor { // prettier-ignore diff --git a/src/type/keyof/index.ts b/src/type/keyof/index.ts index 67512251b..f0c1a32cf 100644 --- a/src/type/keyof/index.ts +++ b/src/type/keyof/index.ts @@ -29,4 +29,5 @@ THE SOFTWARE. export * from './keyof-from-mapped-result' export * from './keyof-property-entries' export * from './keyof-property-keys' +export * from './keyof-ref' export * from './keyof' diff --git a/src/type/keyof/keyof-from-mapped-result.ts b/src/type/keyof/keyof-from-mapped-result.ts index 9991b7c15..f4b90bfa8 100644 --- a/src/type/keyof/keyof-from-mapped-result.ts +++ b/src/type/keyof/keyof-from-mapped-result.ts @@ -30,55 +30,55 @@ import type { SchemaOptions } from '../schema/index' import type { Ensure, Evaluate } from '../helpers/index' import type { TProperties } from '../object/index' import { MappedResult, type TMappedResult } from '../mapped/index' -import { KeyOf, type TKeyOf } from './keyof' +import { KeyOf, type TKeyOfUnion } from './keyof' import { Clone } from '../clone/value' // ------------------------------------------------------------------ // FromProperties // ------------------------------------------------------------------ // prettier-ignore type TFromProperties< - K extends TProperties + Properties extends TProperties > = ( - { [K2 in keyof K]: TKeyOf } + { [K2 in keyof Properties]: TKeyOfUnion } ) // prettier-ignore function FromProperties< - K extends TProperties ->(K: K, options?: SchemaOptions): TFromProperties { - const Acc = {} as TProperties - for(const K2 of globalThis.Object.getOwnPropertyNames(K)) Acc[K2] = KeyOf(K[K2], Clone(options)) - return Acc as never + Properties extends TProperties +>(properties: Properties, options?: SchemaOptions): TFromProperties { + const result = {} as TProperties + for(const K2 of globalThis.Object.getOwnPropertyNames(properties)) result[K2] = KeyOf(properties[K2], Clone(options)) + return result as never } // ------------------------------------------------------------------ // FromMappedResult // ------------------------------------------------------------------ // prettier-ignore type TFromMappedResult< - R extends TMappedResult + MappedResult extends TMappedResult > = ( - Evaluate> + Evaluate> ) // prettier-ignore function FromMappedResult< - R extends TMappedResult ->(R: R, options?: SchemaOptions): TFromMappedResult { - return FromProperties(R.properties, options) as never + MappedResult extends TMappedResult +>(mappedResult: MappedResult, options?: SchemaOptions): TFromMappedResult { + return FromProperties(mappedResult.properties, options) as never } // ------------------------------------------------------------------ // KeyOfFromMappedResult // ------------------------------------------------------------------ // prettier-ignore export type TKeyOfFromMappedResult< - R extends TMappedResult, - P extends TProperties = TFromMappedResult + MappedResult extends TMappedResult, + Properties extends TProperties = TFromMappedResult > = ( - Ensure> + Ensure> ) // prettier-ignore export function KeyOfFromMappedResult< - R extends TMappedResult, - P extends TProperties = TFromMappedResult ->(R: R, options?: SchemaOptions): TMappedResult

{ - const P = FromMappedResult(R, options) - return MappedResult(P) as never + MappedResult extends TMappedResult, + Properties extends TProperties = TFromMappedResult +>(mappedResult: MappedResult, options?: SchemaOptions): TMappedResult { + const properties = FromMappedResult(mappedResult, options) + return MappedResult(properties) as never } diff --git a/src/type/keyof/keyof-ref.ts b/src/type/keyof/keyof-ref.ts new file mode 100644 index 000000000..47acfceef --- /dev/null +++ b/src/type/keyof/keyof-ref.ts @@ -0,0 +1,41 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2024 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 { Kind } from '../symbols/symbols' +import { TSchema, SchemaOptions } from '../schema/schema' +import { CreateType } from '../create/index' + +export interface TKeyOfRef extends TSchema { + [Kind]: 'KeyOfRef' + static: unknown + $ref: Ref +} +/** Creates a KeyOfRef Ref */ +export function KeyOfRef($ref: Ref, options?: SchemaOptions): TKeyOfRef { + return CreateType({ [Kind]: 'KeyOfRef', $ref }, options) as never +} diff --git a/src/type/keyof/keyof.ts b/src/type/keyof/keyof.ts index 2e4c42df6..587e1e07c 100644 --- a/src/type/keyof/keyof.ts +++ b/src/type/keyof/keyof.ts @@ -33,51 +33,60 @@ import type { TMappedResult } from '../mapped/index' import type { SchemaOptions } from '../schema/index' import { Literal, type TLiteral, type TLiteralValue } from '../literal/index' import { Number, type TNumber } from '../number/index' +import { Ref, type TRef } from '../ref/index' import { KeyOfPropertyKeys, type TKeyOfPropertyKeys } from './keyof-property-keys' import { UnionEvaluated, type TUnionEvaluated } from '../union/index' import { KeyOfFromMappedResult, type TKeyOfFromMappedResult } from './keyof-from-mapped-result' +import { KeyOfRef, TKeyOfRef } from './keyof-ref' // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -import { IsMappedResult } from '../guard/kind' +import { IsMappedResult, IsRef } from '../guard/kind' // ------------------------------------------------------------------ // FromPropertyKeys // ------------------------------------------------------------------ // prettier-ignore -export type TKeyOfPropertyKeysToRest = ( - T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] +export type TKeyOfPropertyKeysToRest = ( + PropertyKeys extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] ? L extends '[number]' - ? TKeyOfPropertyKeysToRest - : TKeyOfPropertyKeysToRest>]> - : Acc + ? TKeyOfPropertyKeysToRest + : TKeyOfPropertyKeysToRest>]> + : Result ) // prettier-ignore -export function KeyOfPropertyKeysToRest(T: [...T]): TKeyOfPropertyKeysToRest { - return T.map(L => L === '[number]' ? Number() : Literal(L as TLiteralValue)) as never +export function KeyOfPropertyKeysToRest(propertyKeys: [...PropertyKeys]): TKeyOfPropertyKeysToRest { + return propertyKeys.map(L => L === '[number]' ? Number() : Literal(L as TLiteralValue)) as never } // ------------------------------------------------------------------ -// KeyOfTypeResolve +// TKeyOfUnion // ------------------------------------------------------------------ // prettier-ignore -export type TKeyOf< - T extends TSchema, - K extends PropertyKey[] = TKeyOfPropertyKeys, - S extends TSchema[] = TKeyOfPropertyKeysToRest, - U = TUnionEvaluated -> = ( - Ensure +export type TKeyOfUnion< + Type extends TSchema, + PropertyKeys extends PropertyKey[] = TKeyOfPropertyKeys, + PropertyKeyTypes extends TSchema[] = TKeyOfPropertyKeysToRest, + Result = TUnionEvaluated +> = Ensure + +// ------------------------------------------------------------------ +// TKeyOf +// ------------------------------------------------------------------ +// prettier-ignore +export type TKeyOf = ( + Type extends TRef ? TKeyOfRef : + Type extends TMappedResult ? TKeyOfFromMappedResult : + TKeyOfUnion ) /** `[Json]` Creates a KeyOf type */ -export function KeyOf(T: T, options?: SchemaOptions): TKeyOfFromMappedResult -/** `[Json]` Creates a KeyOf type */ -export function KeyOf(T: T, options?: SchemaOptions): TKeyOf -/** `[Json]` Creates a KeyOf type */ -export function KeyOf(T: TSchema, options?: SchemaOptions): never { - if (IsMappedResult(T)) { - return KeyOfFromMappedResult(T, options) as never +export function KeyOf(type: Type, options?: SchemaOptions): TKeyOf { + // Note: This type has become overwhelmed with various overloads. Refactor in next revision. + if (IsRef(type)) { + return KeyOfRef(type.$ref, options) as never + } else if (IsMappedResult(type)) { + return KeyOfFromMappedResult(type, options) as never } else { - const K = KeyOfPropertyKeys(T) + const K = KeyOfPropertyKeys(type) as PropertyKey[] const S = KeyOfPropertyKeysToRest(K) const U = UnionEvaluated(S) return CreateType(U, options) as never diff --git a/src/type/module/compute.ts b/src/type/module/compute.ts new file mode 100644 index 000000000..dc48f382d --- /dev/null +++ b/src/type/module/compute.ts @@ -0,0 +1,345 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2024 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 { CreateType } from '../create/index' +import { Discard } from '../discard/index' +import { Ensure, Evaluate } from '../helpers/index' +import { type TSchema } from '../schema/index' +import { Array, type TArray } from '../array/index' +import { Awaited, type TAwaited } from '../awaited/index' +import { AsyncIterator, type TAsyncIterator } from '../async-iterator/index' +import { TComputed } from '../computed/index' +import { Constructor, type TConstructor } from '../constructor/index' +import { Function, type TFunction } from '../function/index' +import { Intersect, type TIntersect, type TIntersectEvaluated } from '../intersect/index' +import { Iterator, type TIterator } from '../iterator/index' +import { KeyOf, type TKeyOf, type TKeyOfRef } from '../keyof/index' +import { Object, type TObject, type TProperties } from '../object/index' +import { Omit, type TOmit } from '../omit/index' +import { Pick, type TPick } from '../pick/index' +import { Never, type TNever } from '../never/index' +import { Partial, TPartial } from '../partial/index' +import { Ref, type TRef } from '../ref/index' +import { Required, TRequired, TRequiredRef } from '../required/index' +import { Tuple, type TTuple } from '../tuple/index' +import { Union, type TUnion, type TUnionEvaluated } from '../union/index' + +// ------------------------------------------------------------------ +// KindGuard +// ------------------------------------------------------------------ +import * as KindGuard from '../guard/kind' + + +// ------------------------------------------------------------------ +// Deref +// +// Derferences a type. This type must only be used in the context +// of dereferencing TComputed parameters. Attempting to deref +// outside parameters will result in stack overflow. +// ------------------------------------------------------------------ +// prettier-ignore +type TDerefParameters = ( + Types extends [infer Left extends TSchema, ...infer Right extends TSchema[]] + ? Left extends TRef + ? TDerefParameters]> + : TDerefParameters]> + : Result +) +// prettier-ignore +function DerefParameters(moduleProperties: ModuleProperties, types: [...Types]): TFromRest { + return types.map((type) => { + return KindGuard.IsRef(type) + ? Deref(moduleProperties, type.$ref) + : FromType(moduleProperties, type) + }) as never +} +// prettier-ignore +type TDeref + ? TDeref + : TFromType + : TNever +)> = Result +// prettier-ignore +function Deref(moduleProperties: ModuleProperties, ref: Ref): TDeref { + return ( + ref in moduleProperties + ? KindGuard.IsRef(moduleProperties[ref]) + ? Deref(moduleProperties, moduleProperties[ref].$ref) + : FromType(moduleProperties, moduleProperties[ref]) + : Never() + ) as never +} +// ------------------------------------------------------------------ +// Awaited +// ------------------------------------------------------------------ +// prettier-ignore +type TFromAwaited = ( + Parameters extends [infer T0 extends TSchema] ? TAwaited : TNever +) +// prettier-ignore +function FromAwaited(parameters: Parameters): TFromAwaited { + return Awaited(parameters[0]) as never +} +// ------------------------------------------------------------------ +// Partial +// ------------------------------------------------------------------ +// prettier-ignore +type TFromPartial = ( + Parameters extends [infer T0 extends TSchema] ? TPartial : TNever +) +// prettier-ignore +function FromPartial(parameters: Parameters): TFromPartial { + return Partial(parameters[0]) as never +} +// ------------------------------------------------------------------ +// Omit +// ------------------------------------------------------------------ +// prettier-ignore +type TFromOmit = ( + Parameters extends [infer T0 extends TSchema, infer T1 extends TSchema] ? TOmit : TNever +) +// prettier-ignore +function FromOmit(parameters: Parameters): TFromPick { + return Omit(parameters[0], parameters[1]) as never +} +// ------------------------------------------------------------------ +// Pick +// ------------------------------------------------------------------ +// prettier-ignore +type TFromPick = ( + Parameters extends [infer T0 extends TSchema, infer T1 extends TSchema] ? TPick : TNever +) +// prettier-ignore +function FromPick(parameters: Parameters): TFromPick { + return Pick(parameters[0], parameters[1]) as never +} +// ------------------------------------------------------------------ +// ComputeComputed +// ------------------------------------------------------------------ +// prettier-ignore +type TFromComputed +> = ( + Target extends 'Awaited' ? TFromAwaited : + Target extends 'Partial' ? TFromPartial : + Target extends 'Omit' ? TFromOmit : + Target extends 'Pick' ? TFromPick : + TNever +) +// prettier-ignore +function FromComputed(moduleProperties: ModuleProperties, target: Target, parameters: Parameters): TFromComputed { + const dereferenced = DerefParameters(moduleProperties, parameters) + return ( + target === 'Awaited' ? FromAwaited(dereferenced) : + target === 'Partial' ? FromPartial(dereferenced) : + target === 'Omit' ? FromOmit(dereferenced) : + target === 'Pick' ? FromPick(dereferenced) : + Never() + ) as never +} +// ------------------------------------------------------------------ +// Object +// ------------------------------------------------------------------ +// prettier-ignore +type TFromObject = Ensure +}>>> +function FromObject(moduleProperties: ModuleProperties, properties: Properties): TFromObject { + return Object( + globalThis.Object.keys(properties).reduce((result, key) => { + return { ...result, [key]: FromType(moduleProperties, properties[key]) as TSchema } + }, {} as TProperties), + ) as never +} +// ------------------------------------------------------------------ +// Constructor +// ------------------------------------------------------------------ +// prettier-ignore +type TFromConstructor = ( + TConstructor, TFromType> +) +// prettier-ignore +function FromConstructor( + moduleProperties: ModuleProperties, + parameters: [...Parameters], + instanceType: InstanceType, +): TFromConstructor { + return Constructor(FromRest(moduleProperties, parameters as never), FromType(moduleProperties, instanceType)) as never +} +// ------------------------------------------------------------------ +// Function +// ------------------------------------------------------------------ +// prettier-ignore +type TFromFunction = Ensure< + Ensure, TFromType>> +> +// prettier-ignore +function FromFunction( + moduleProperties: ModuleProperties, + parameters: [...Parameters], + returnType: ReturnType, +): TFromFunction { + return Function(FromRest(moduleProperties, parameters as never), FromType(moduleProperties, returnType)) as never +} +// ------------------------------------------------------------------ +// Tuple +// ------------------------------------------------------------------ +// prettier-ignore +type TFromTuple = ( + Ensure>> +) +function FromTuple(moduleProperties: ModuleProperties, types: [...Types]): TFromTuple { + return Tuple(FromRest(moduleProperties, types as never)) as never +} +// ------------------------------------------------------------------ +// Intersect +// ------------------------------------------------------------------ +// prettier-ignore +type TFromIntersect = ( + Ensure>> +) +function FromIntersect(moduleProperties: ModuleProperties, types: [...Types]): TFromIntersect { + return Intersect(FromRest(moduleProperties, types as never)) as never +} +// ------------------------------------------------------------------ +// Union +// ------------------------------------------------------------------ +// prettier-ignore +type TFromUnion = ( + Ensure>> +) +function FromUnion(moduleProperties: ModuleProperties, types: [...Types]): TFromUnion { + return Union(FromRest(moduleProperties, types as never)) as never +} +// ------------------------------------------------------------------ +// Array +// ------------------------------------------------------------------ +// prettier-ignore +type TFromArray = ( + Ensure>> +) +function FromArray(moduleProperties: ModuleProperties, type: Type): TFromArray { + return Array(FromType(moduleProperties, type)) +} +// ------------------------------------------------------------------ +// AsyncIterator +// ------------------------------------------------------------------ +// prettier-ignore +type TFromAsyncIterator = ( + TAsyncIterator> +) +function FromAsyncIterator(moduleProperties: ModuleProperties, type: Type): TFromAsyncIterator { + return AsyncIterator(FromType(moduleProperties, type)) +} +// ------------------------------------------------------------------ +// Iterator +// ------------------------------------------------------------------ +// prettier-ignore +type TFromIterator = ( + TIterator> +) +function FromIterator(moduleProperties: ModuleProperties, type: Type): TFromIterator { + return Iterator(FromType(moduleProperties, type)) +} +// ------------------------------------------------------------------ +// Rest +// ------------------------------------------------------------------ +// prettier-ignore +type TFromRest = ( + Types extends [infer Left extends TSchema, ...infer Right extends TSchema[]] + ? TFromRest]> + : Result +) +function FromRest(moduleProperties: ModuleProperties, types: [...Types]): TFromRest { + return types.map((type) => FromType(moduleProperties, type)) as never +} +// ------------------------------------------------------------------ +// Type +// ------------------------------------------------------------------ +// prettier-ignore +export type TFromType = ( + Type extends TComputed ? TFromComputed : + Type extends TObject ? TFromObject : + Type extends TConstructor ? TFromConstructor : + Type extends TFunction ? TFromFunction : + Type extends TTuple ? TFromTuple : + Type extends TIntersect ? TFromIntersect : + Type extends TUnion ? TFromUnion : + Type extends TArray ? TFromArray : + Type extends TAsyncIterator ? TFromAsyncIterator : + Type extends TIterator ? TFromIterator : + Type +) +// prettier-ignore +export function FromType(moduleProperties: ModuleProperties, type: Type): TFromType { + return ( + KindGuard.IsComputed(type) ? CreateType(FromComputed(moduleProperties, type.target, type.parameters)) : + KindGuard.IsObject(type) ? CreateType(FromObject(moduleProperties, type.properties), type) : + KindGuard.IsConstructor(type) ? CreateType(FromConstructor(moduleProperties, type.parameters, type.returns), type) : + KindGuard.IsFunction(type) ? CreateType(FromFunction(moduleProperties, type.parameters, type.returns), type) : + KindGuard.IsTuple(type)? CreateType(FromTuple(moduleProperties, type.items || []), type) : + KindGuard.IsIntersect(type) ? CreateType(FromIntersect(moduleProperties, type.allOf), type) : + KindGuard.IsUnion(type) ? CreateType(FromUnion(moduleProperties, type.anyOf), type) : + KindGuard.IsArray(type) ? CreateType(FromArray(moduleProperties, type.items), type) : + KindGuard.IsAsyncIterator(type) ? CreateType(FromAsyncIterator(moduleProperties, type.items), type) : + KindGuard.IsIterator(type) ? CreateType(FromIterator(moduleProperties, type.items), type) : + type + ) as never +} +// ------------------------------------------------------------------ +// ComputeType +// ------------------------------------------------------------------ +// prettier-ignore +export type TComputeType = ( + Key extends keyof ModuleProperties + ? TFromType + : TNever +) +// prettier-ignore +export function ComputeType(moduleProperties: ModuleProperties, key: Key): TComputeType { + return ( + key in moduleProperties + ? FromType(moduleProperties, moduleProperties[key as keyof ModuleProperties]) + : Never() + ) as never +} +// ------------------------------------------------------------------ +// ComputeModuleProperties +// ------------------------------------------------------------------ +// prettier-ignore +export type TComputeModuleProperties = Evaluate<{ + [Key in keyof ModuleProperties]: TComputeType +}> +// prettier-ignore +export function ComputeModuleProperties(moduleProperties: ModuleProperties): TComputeModuleProperties { + return globalThis.Object.getOwnPropertyNames(moduleProperties).reduce((result, key) => { + return {...result, [key]: ComputeType(moduleProperties, key) } + }, {} as TProperties) as never +} diff --git a/src/type/module/infer.ts b/src/type/module/infer.ts new file mode 100644 index 000000000..2500e12ba --- /dev/null +++ b/src/type/module/infer.ts @@ -0,0 +1,162 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2024 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 { Ensure, Evaluate } from '../helpers/index' +import { TSchema } from '../schema/index' +import { TObject, TProperties } from '../object/index' +import { TConstructor } from '../constructor/index' +import { TFunction } from '../function/index' +import { TTuple } from '../tuple/index' +import { TIntersect } from '../intersect/index' +import { TUnion } from '../union/index' +import { TArray } from '../array/index' +import { TAsyncIterator } from '../async-iterator/index' +import { TIterator } from '../iterator/index' +import { TOptional } from '../optional/index' +import { TReadonly } from '../readonly/index' +import { TRef } from '../ref/index' +import { Static } from '../static/index' + +// ------------------------------------------------------------------ +// Ref +// ------------------------------------------------------------------ +// prettier-ignore +type TInferRef = ( + Ref extends keyof ModuleProperties ? TInfer : unknown +) +// ------------------------------------------------------------------ +// Object +// ------------------------------------------------------------------ +type ReadonlyOptionalPropertyKeys = { [K in keyof Properties]: Properties[K] extends TReadonly ? (Properties[K] extends TOptional ? K : never) : never }[keyof Properties] +type ReadonlyPropertyKeys = { [K in keyof Source]: Source[K] extends TReadonly ? (Source[K] extends TOptional ? never : K) : never }[keyof Source] +type OptionalPropertyKeys = { [K in keyof Source]: Source[K] extends TOptional ? (Source[K] extends TReadonly ? never : K) : never }[keyof Source] +type RequiredPropertyKeys = keyof Omit | ReadonlyPropertyKeys | OptionalPropertyKeys> +// prettier-ignore +type InferPropertiesWithModifiers> = Evaluate<( + Readonly>>> & + Readonly>> & + Partial>> & + Required>> +)> +// prettier-ignore +type InferProperties = InferPropertiesWithModifiers +}> +// prettier-ignore +type TInferObject = ( + InferProperties +) +// ------------------------------------------------------------------ +// Constructor +// ------------------------------------------------------------------ +// prettier-ignore +type TInferConstructor = Ensure< + new (...args: TInferTuple) => TInfer +> +// ------------------------------------------------------------------ +// Function +// ------------------------------------------------------------------ +// prettier-ignore +type TInferFunction = Ensure< + (...args: TInferTuple) => TInfer +> +// ------------------------------------------------------------------ +// Tuple +// ------------------------------------------------------------------ +// prettier-ignore +type TInferTuple = ( + Types extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? TInferTuple]> + : Result +) +// ------------------------------------------------------------------ +// Intersect +// ------------------------------------------------------------------ +// prettier-ignore +type TInferIntersect = ( + Types extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? TInferIntersect> + : Result +) +// ------------------------------------------------------------------ +// Union +// ------------------------------------------------------------------ +// prettier-ignore +type TInferUnion = ( + Types extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? TInferUnion> + : Result +) +// ------------------------------------------------------------------ +// Array +// ------------------------------------------------------------------ +// prettier-ignore +type TInferArray = ( + Ensure>> +) +// ------------------------------------------------------------------ +// AsyncIterator +// ------------------------------------------------------------------ +// prettier-ignore +type TInferAsyncIterator = ( + Ensure>> +) +// ------------------------------------------------------------------ +// Iterator +// ------------------------------------------------------------------ +// prettier-ignore +type TInferIterator = ( + Ensure>> +) +// ------------------------------------------------------------------ +// Infer +// ------------------------------------------------------------------ +// prettier-ignore +type TInfer = ( + Type extends TRef ? TInferRef : + Type extends TObject ? TInferObject : + Type extends TConstructor ? TInferConstructor : + Type extends TFunction ? TInferFunction : + Type extends TTuple ? TInferTuple : + Type extends TIntersect ? TInferIntersect : + Type extends TUnion ? TInferUnion : + Type extends TArray ? TInferArray : + Type extends TAsyncIterator ? TInferAsyncIterator : + Type extends TIterator ? TInferIterator : + Static +) +// ------------------------------------------------------------------ +// InferImport +// ------------------------------------------------------------------ +/** Inference Path for Imports. This type is used to compute TImport `static` */ +// prettier-ignore +export type TInferType = ( + Key extends keyof ModuleProperties + ? TInfer + : never +) diff --git a/src/type/module/module.ts b/src/type/module/module.ts index 3fef83c80..2d5431956 100644 --- a/src/type/module/module.ts +++ b/src/type/module/module.ts @@ -26,124 +26,22 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { Ensure } from '../helpers/index' +import { Evaluate } from '../helpers/index' import { CreateType } from '../create/index' import { Kind } from '../symbols/index' import { SchemaOptions, TSchema } from '../schema/index' -import { TObject, TProperties } from '../object/index' -import { TConstructor } from '../constructor/index' -import { TFunction } from '../function/index' -import { TTuple } from '../tuple/index' -import { TIntersect } from '../intersect/index' -import { TUnion } from '../union/index' -import { TArray } from '../array/index' -import { TAsyncIterator } from '../async-iterator/index' -import { TIterator } from '../iterator/index' -import { TLiteral, TLiteralValue } from '../literal/index' -import { TAny } from '../any/index' -import { TBigInt } from '../bigint/index' -import { TBoolean } from '../boolean/index' -import { TDate } from '../date/index' -import { TInteger } from '../integer/index' -import { TNever } from '../never/index' -import { TNumber } from '../number/index' -import { TNull } from '../null/index' -import { TRef } from '../ref/index' -import { TRegExp } from '../regexp/index' -import { TString } from '../string/index' -import { TSymbol } from '../symbol/index' -import { TTemplateLiteral, TTemplateLiteralKind } from '../template-literal/index' -import { TUint8Array } from '../uint8array/index' -import { TUndefined } from '../undefined/index' -import { TUnknown } from '../unknown/index' -import { TVoid } from '../void/index' +import { TProperties } from '../object/index' import { Static } from '../static/index' // ------------------------------------------------------------------ -// Infer +// Module Infrastructure Types // ------------------------------------------------------------------ -// prettier-ignore -type InferImport = ( - Infer -) -// prettier-ignore -type InferRef = ( - Ref extends keyof Properties ? Infer : never -) -// prettier-ignore -type InferObject = { - [K in keyof Properties]: Infer -} & {} -// prettier-ignore -type InferConstructor = Ensure< - new (...args: InferTuple) => Infer -> -// prettier-ignore -type InferFunction = Ensure< - (...args: InferTuple) => Infer -> -// prettier-ignore -type InferTuple = ( - Types extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? InferTuple]> - : Result -) -// prettier-ignore -type InferIntersect = ( - Types extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? InferIntersect> - : Result -) -// prettier-ignore -type InferUnion = ( - Types extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? InferUnion> - : Result -) -// prettier-ignore -type InferArray = ( - Ensure>> -) -// prettier-ignore -type InferAsyncIterator = ( - Ensure>> -) -// prettier-ignore -type InferIterator = ( - Ensure>> -) -// prettier-ignore -type Infer = ( - Type extends TImport ? InferImport : - Type extends TRef ? InferRef : - Type extends TObject ? InferObject : - Type extends TConstructor ? InferConstructor : - Type extends TFunction ? InferFunction : - Type extends TTuple ? InferTuple : - Type extends TIntersect ? InferIntersect : - Type extends TUnion ? InferUnion : - Type extends TArray ? InferArray : - Type extends TAsyncIterator ? InferAsyncIterator : - Type extends TIterator ? InferIterator : - Type extends TTemplateLiteral ? Static> : - Type extends TLiteral ? S : - Type extends TAny ? any : - Type extends TBigInt ? bigint : - Type extends TBoolean ? boolean : - Type extends TDate ? Date : - Type extends TInteger ? number : - Type extends TNever ? never : - Type extends TNumber ? number : - Type extends TRegExp ? string : - Type extends TString ? string : - Type extends TSymbol ? symbol : - Type extends TNull ? null : - Type extends TUint8Array ? Uint8Array : - Type extends TUndefined ? undefined : - Type extends TUnknown ? unknown : - Type extends TVoid ? void : - never -) +import { ComputeType, type TComputeType } from './compute' +import { ComputeModuleProperties, TComputeModuleProperties } from './compute' + +import { TInferType } from './infer' +import { TNever } from '../never' + // ------------------------------------------------------------------ // Definitions // ------------------------------------------------------------------ @@ -155,32 +53,32 @@ export interface TDefinitions extends TSch // Import // ------------------------------------------------------------------ // prettier-ignore -export interface TImport extends TSchema { +export interface TImport extends TSchema { [Kind]: 'Import' - static: InferImport - $defs: ModuleProperties + static: TInferType + $defs: TComputeType $ref: Key } // ------------------------------------------------------------------ // Module // ------------------------------------------------------------------ // prettier-ignore -export class TModule { - constructor(private readonly $defs: Properties, private readonly options: SchemaOptions = {}) {} - - /** `[Json]` Returns the Type definitions for this module */ - public Defs(): TDefinitions { - return CreateType({ $defs: this.ResolveDefinitionsWithIdentifiers() }, this.options) as never +export class TModule> { + private readonly $defs: ComputedModuleProperties + constructor($defs: ModuleProperties) { + const computed = ComputeModuleProperties($defs) + const identified = this.WithIdentifiers(computed as never) + this.$defs = identified as never } /** `[Json]` Imports a Type by Key. */ - public Import(key: Key, options?: SchemaOptions): TImport { - return CreateType({ [Kind]: 'Import', $defs: this.ResolveDefinitionsWithIdentifiers(), $ref: key }, options) as never + public Import(key: Key, options?: SchemaOptions): TImport { + return CreateType({ [Kind]: 'Import', $defs: this.$defs, $ref: key }, options) as never } - /** `[Internal]` For each definition, assign an `$id` property. */ - private ResolveDefinitionsWithIdentifiers() { - return globalThis.Object.getOwnPropertyNames(this.$defs).reduce((Result, Key) => ( - { ...Result, [Key]: { ...this.$defs[Key], $id: Key }} - ), {}) + // prettier-ignore + private WithIdentifiers($defs: ModuleProperties) { + return globalThis.Object.getOwnPropertyNames($defs).reduce((result, key) => { + return { ...result, [key]: { ...$defs[key], $id: key }} + }, {}) } } /** `[Json]` Creates a Type Definition Module. */ diff --git a/src/type/omit/omit-from-mapped-key.ts b/src/type/omit/omit-from-mapped-key.ts index 5eeb68660..2a2a4ae94 100644 --- a/src/type/omit/omit-from-mapped-key.ts +++ b/src/type/omit/omit-from-mapped-key.ts @@ -36,77 +36,52 @@ import { Clone } from '../clone/value' // FromPropertyKey // ------------------------------------------------------------------ // prettier-ignore -type TFromPropertyKey< - T extends TSchema, - K extends PropertyKey, -> = { - [_ in K]: TOmit - } +type TFromPropertyKey = { + [_ in Key]: TOmit +} // prettier-ignore -function FromPropertyKey< - T extends TSchema, - K extends PropertyKey, ->(T: T, K: K, options?: SchemaOptions): TFromPropertyKey { - return { - [K]: Omit(T, [K], Clone(options)) - } as never +function FromPropertyKey(type: Type, key: Key, options?: SchemaOptions): TFromPropertyKey { + return { [key]: Omit(type, [key], Clone(options)) } as never } // ------------------------------------------------------------------ // FromPropertyKeys // ------------------------------------------------------------------ // prettier-ignore -type TFromPropertyKeys< - T extends TSchema, - K extends PropertyKey[], - Acc extends TProperties = {} -> = ( - K extends [infer LK extends PropertyKey, ...infer RK extends PropertyKey[]] - ? TFromPropertyKeys> - : Acc +type TFromPropertyKeys = ( + PropertyKeys extends [infer LK extends PropertyKey, ...infer RK extends PropertyKey[]] + ? TFromPropertyKeys> + : Result ) // prettier-ignore -function FromPropertyKeys< - T extends TSchema, - K extends PropertyKey[] ->(T: T, K: [...K], options?: SchemaOptions): TFromPropertyKeys { - return K.reduce((Acc, LK) => { - return { ...Acc, ...FromPropertyKey(T, LK, options) } +function FromPropertyKeys(type: Type, propertyKeys: [...PropertyKeys], options?: SchemaOptions): TFromPropertyKeys { + return propertyKeys.reduce((Acc, LK) => { + return { ...Acc, ...FromPropertyKey(type, LK, options) } }, {} as TProperties) as never } // ------------------------------------------------------------------ // FromMappedKey // ------------------------------------------------------------------ // prettier-ignore -type TFromMappedKey< - T extends TSchema, - K extends TMappedKey, -> = ( - TFromPropertyKeys +type TFromMappedKey = ( + TFromPropertyKeys ) // prettier-ignore -function FromMappedKey< - T extends TSchema, - K extends TMappedKey, ->(T: T, K: K, options?: SchemaOptions): TFromMappedKey { - return FromPropertyKeys(T, K.keys, options) as never +function FromMappedKey(type: Type, mappedKey: MappedKey, options?: SchemaOptions): TFromMappedKey { + return FromPropertyKeys(type, mappedKey.keys, options) as never } // ------------------------------------------------------------------ // OmitFromMappedKey // ------------------------------------------------------------------ // prettier-ignore -export type TOmitFromMappedKey< - T extends TSchema, - K extends TMappedKey, - P extends TProperties = TFromMappedKey +export type TOmitFromMappedKey > = ( - TMappedResult

+ TMappedResult ) // prettier-ignore -export function OmitFromMappedKey< - T extends TSchema, - K extends TMappedKey, - P extends TProperties = TFromMappedKey ->(T: T, K: K, options?: SchemaOptions): TMappedResult

{ - const P = FromMappedKey(T, K, options) - return MappedResult(P) as never +export function OmitFromMappedKey +>(type: Type, mappedKey: MappedKey, options?: SchemaOptions): TMappedResult { + const properties = FromMappedKey(type, mappedKey, options) + return MappedResult(properties) as never } diff --git a/src/type/omit/omit-from-mapped-result.ts b/src/type/omit/omit-from-mapped-result.ts index 205047ea8..5abac5f60 100644 --- a/src/type/omit/omit-from-mapped-result.ts +++ b/src/type/omit/omit-from-mapped-result.ts @@ -37,55 +37,43 @@ import { Clone } from '../clone/value' // FromProperties // ------------------------------------------------------------------ // prettier-ignore -type TFromProperties< - P extends TProperties, - K extends PropertyKey[], -> = ( - { [K2 in keyof P]: TOmit } +type TFromProperties = ( + { [K2 in keyof Properties]: TOmit } ) // prettier-ignore -function FromProperties< - P extends TProperties, - K extends PropertyKey[], ->(P: P, K: [...K], options?: SchemaOptions): TFromProperties { - const Acc = {} as TProperties - for(const K2 of globalThis.Object.getOwnPropertyNames(P)) Acc[K2] = Omit(P[K2], K, Clone(options)) - return Acc as never +function FromProperties(properties: Properties, propertyKeys: [...PropertyKeys], options?: SchemaOptions): TFromProperties { + const result = {} as TProperties + for(const K2 of globalThis.Object.getOwnPropertyNames(properties)) result[K2] = Omit(properties[K2], propertyKeys, Clone(options)) + return result as never } // ------------------------------------------------------------------ // FromMappedResult // ------------------------------------------------------------------ // prettier-ignore -type TFromMappedResult< - R extends TMappedResult, - K extends PropertyKey[], -> = ( - Evaluate> +type TFromMappedResult = ( + Evaluate> ) // prettier-ignore -function FromMappedResult< - R extends TMappedResult, - K extends PropertyKey[] ->(R: R, K: [...K], options?: SchemaOptions): TFromMappedResult { - return FromProperties(R.properties, K, options) as never +function FromMappedResult(mappedResult: MappedResult, propertyKeys: [...PropertyKeys], options?: SchemaOptions): TFromMappedResult { + return FromProperties(mappedResult.properties, propertyKeys, options) as never } // ------------------------------------------------------------------ // TOmitFromMappedResult // ------------------------------------------------------------------ // prettier-ignore export type TOmitFromMappedResult< - T extends TMappedResult, - K extends PropertyKey[], - P extends TProperties = TFromMappedResult + MappedResult extends TMappedResult, + PropertyKeys extends PropertyKey[], + Properties extends TProperties = TFromMappedResult > = ( - Ensure> + Ensure> ) // prettier-ignore export function OmitFromMappedResult< - R extends TMappedResult, - K extends PropertyKey[], - P extends TProperties = TFromMappedResult ->(R: R, K: [...K], options?: SchemaOptions): TMappedResult

{ - const P = FromMappedResult(R, K, options) - return MappedResult(P) as never + MappedResult extends TMappedResult, + PropertyKeys extends PropertyKey[], + Properties extends TProperties = TFromMappedResult +>(mappedResult: MappedResult, propertyKeys: [...PropertyKeys], options?: SchemaOptions): TMappedResult { + const properties = FromMappedResult(mappedResult, propertyKeys, options) + return MappedResult(properties) as never } diff --git a/src/type/omit/omit.ts b/src/type/omit/omit.ts index 99a947706..afba211c2 100644 --- a/src/type/omit/omit.ts +++ b/src/type/omit/omit.ts @@ -28,112 +28,165 @@ THE SOFTWARE. import { CreateType } from '../create/type' import { Discard } from '../discard/discard' +import { TransformKind } from '../symbols/symbols' import type { SchemaOptions, TSchema } from '../schema/index' import type { TupleToUnion, Evaluate, Ensure } from '../helpers/index' import { type TRecursive } from '../recursive/index' import type { TMappedKey, TMappedResult } from '../mapped/index' +import { Computed, TComputed } from '../computed/index' +import { Literal, TLiteral, TLiteralValue } from '../literal/index' +import { IndexPropertyKeys, type TIndexPropertyKeys } from '../indexed/index' import { Intersect, type TIntersect } from '../intersect/index' import { Union, type TUnion } from '../union/index' import { Object, type TObject, type TProperties } from '../object/index' -import { IndexPropertyKeys, type TIndexPropertyKeys } from '../indexed/index' +import { type TRef } from '../ref/index' + +// ------------------------------------------------------------------ +// Mapped +// ------------------------------------------------------------------ import { OmitFromMappedKey, type TOmitFromMappedKey } from './omit-from-mapped-key' import { OmitFromMappedResult, type TOmitFromMappedResult } from './omit-from-mapped-result' -import { TransformKind } from '../symbols/symbols' + // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -import { IsMappedKey, IsIntersect, IsUnion, IsObject, IsSchema, IsMappedResult } from '../guard/kind' +import { IsMappedKey, IsIntersect, IsUnion, IsObject, IsSchema, IsMappedResult, IsLiteralValue, IsRef } from '../guard/kind' +import { IsArray as IsArrayValue } from '../guard/value' // ------------------------------------------------------------------ // FromIntersect // ------------------------------------------------------------------ // prettier-ignore -type TFromIntersect = ( - T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? TFromIntersect]> - : Acc +type TFromIntersect = ( + Types extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? TFromIntersect]> + : Result ) // prettier-ignore -function FromIntersect(T: T, K: K) { - return T.map((T) => OmitResolve(T, K)) as never +function FromIntersect(types: Types, propertyKeys: PropertyKeys) { + return types.map((type) => OmitResolve(type, propertyKeys)) as never } // ------------------------------------------------------------------ // FromUnion // ------------------------------------------------------------------ // prettier-ignore -type TFromUnion = ( +type TFromUnion = ( T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? TFromUnion]> - : Acc + ? TFromUnion]> + : Result ) // prettier-ignore -function FromUnion(T: T, K: K) { - return T.map((T) => OmitResolve(T, K)) as never +function FromUnion(types: Types, propertyKeys: PropertyKeys) { + return types.map((type) => OmitResolve(type, propertyKeys)) as never } // ------------------------------------------------------------------ // FromProperty // ------------------------------------------------------------------ // prettier-ignore -function FromProperty, K extends PropertyKey>(T: T, K: K): TProperties { - const { [K]: _, ...R } = T +function FromProperty(properties: Properties, key: Key): TProperties { + const { [key]: _, ...R } = properties return R } // prettier-ignore -type TFromProperties> = Evaluate> +type TFromProperties> = ( + Evaluate> +) // prettier-ignore -function FromProperties(T: T, K: K) { - return K.reduce((T, K2) => FromProperty(T, K2), T as TProperties) +function FromProperties(properties: Properties, propertyKeys: PropertyKeys) { + return propertyKeys.reduce((T, K2) => FromProperty(T, K2), properties as TProperties) } // ------------------------------------------------------------------ // FromObject // ------------------------------------------------------------------ // prettier-ignore -type TFromObject = Ensure +type TFromObject = Ensure )>> // prettier-ignore -function FromObject(T: T, K: K): TFromObject { - const options = Discard(T, [TransformKind, '$id', 'required', 'properties']) - const properties = FromProperties(T['properties'], K) - return Object(properties, options) as never +function FromObject(properties: Properties, propertyKeys: PropertyKeys): TFromObject { + const options = Discard(properties, [TransformKind, '$id', 'required', 'properties']) + const omittedProperties = FromProperties(properties['properties'], propertyKeys) + return Object(omittedProperties, options) as never } // ------------------------------------------------------------------ -// OmitResolve +// UnionFromPropertyKeys // ------------------------------------------------------------------ // prettier-ignore -function OmitResolve(T: T, K: [...K]): TOmit { - return ( - IsIntersect(T) ? Intersect(FromIntersect(T.allOf, K)) : - IsUnion(T) ? Union(FromUnion(T.anyOf, K)) : - IsObject(T) ? FromObject(T, K) : - Object({}) - ) as never +type TUnionFromPropertyKeys = ( + PropertyKeys extends [infer Key extends PropertyKey, ...infer Rest extends PropertyKey[]] + ? Key extends TLiteralValue + ? TUnionFromPropertyKeys]> + : TUnionFromPropertyKeys + : TUnion +) +// prettier-ignore +function UnionFromPropertyKeys(propertyKeys: PropertyKeys): TUnionFromPropertyKeys { + const result = propertyKeys.reduce((result, key) => IsLiteralValue(key) ? [...result, Literal(key)]: result, [] as TLiteral[]) + return Union(result) as never } +// ------------------------------------------------------------------ +// TOmitResolve +// ------------------------------------------------------------------ // prettier-ignore -export type TOmit = ( - T extends TRecursive ? TRecursive> : - T extends TIntersect ? TIntersect> : - T extends TUnion ? TUnion> : - T extends TObject ? TFromObject, K> : +export type TOmitResolve = ( + Properties extends TRecursive ? TRecursive> : + Properties extends TIntersect ? TIntersect> : + Properties extends TUnion ? TUnion> : + Properties extends TObject ? TFromObject, PropertyKeys> : TObject<{}> ) +// prettier-ignore +function OmitResolve(properties: Properties, propertyKeys: [...PropertyKeys]): TOmitResolve { + return ( + IsIntersect(properties) ? Intersect(FromIntersect(properties.allOf, propertyKeys)) : + IsUnion(properties) ? Union(FromUnion(properties.anyOf, propertyKeys)) : + IsObject(properties) ? FromObject(properties, propertyKeys) : + Object({}) + ) as never +} // ------------------------------------------------------------------ // TOmit +// +// This mapping logic is to overly complex because of the decision +// to use PropertyKey[] as the default selector. The PropertyKey[] +// did make TMapped types simpler to implement, but a non-TSchema +// selector makes supporting TComputed awkward as it requires +// generalization via TSchema. This type should be reimplemented +// in the next major revision to support TSchema as the primary +// selector. +// // ------------------------------------------------------------------ -/** `[Json]` Constructs a type whose keys are omitted from the given type */ -export function Omit(T: T, K: [...K], options?: SchemaOptions): TOmitFromMappedResult -/** `[Json]` Constructs a type whose keys are omitted from the given type */ -export function Omit(T: T, K: K, options?: SchemaOptions): TOmitFromMappedKey -/** `[Json]` Constructs a type whose keys are omitted from the given type */ -export function Omit>(T: T, K: K, options?: SchemaOptions): TOmit -/** `[Json]` Constructs a type whose keys are omitted from the given type */ -export function Omit(T: T, K: readonly [...K], options?: SchemaOptions): TOmit -export function Omit(T: TSchema, K: any, options?: SchemaOptions): any { - // mapped - if (IsMappedKey(K)) return OmitFromMappedKey(T, K, options) - if (IsMappedResult(T)) return OmitFromMappedResult(T, K, options) - // non-mapped - const I = IsSchema(K) ? IndexPropertyKeys(K) : (K as string[]) - // special: mapping types require overridable options - return CreateType({ ...OmitResolve(T, I), ...options }) -} +// prettier-ignore +export type TOmit : Key, + PropertyKeys extends PropertyKey[] = Key extends TSchema ? TIndexPropertyKeys : Key, + IsTypeRef extends boolean = Type extends TRef ? true : false, + IsKeyRef extends boolean = Key extends TRef ? true : false, +> = ( + Type extends TMappedResult ? TOmitFromMappedResult : + Key extends TMappedKey ? TOmitFromMappedKey : + [IsTypeRef, IsKeyRef] extends [true, true] ? TComputed<'Omit', [Type, TypeKey]> : + [IsTypeRef, IsKeyRef] extends [false, true] ? TComputed<'Omit', [Type, TypeKey]> : + [IsTypeRef, IsKeyRef] extends [true, false] ? TComputed<'Omit', [Type, TypeKey]> : + TOmitResolve +) +/** `[Json]` Constructs a type whose keys are picked from the given type */ +export function Omit(type: Type, key: readonly [...Key], options?: SchemaOptions): TOmit +/** `[Json]` Constructs a type whose keys are picked from the given type */ +export function Omit(type: Type, key: Key, options?: SchemaOptions): TOmit +/** `[Json]` Constructs a type whose keys are picked from the given type */ +// prettier-ignore +export function Omit(type: any, key: any, options?: SchemaOptions): any { + const typeKey: TSchema = IsArrayValue(key) ? UnionFromPropertyKeys(key as PropertyKey[]) : key + const propertyKeys: PropertyKey[] = IsSchema(key) ? IndexPropertyKeys(key) : key + const isTypeRef: boolean = IsRef(type) + const isKeyRef: boolean = IsRef(key) + return ( + IsMappedResult(type) ? OmitFromMappedResult(type, propertyKeys, options) : + IsMappedKey(key) ? OmitFromMappedKey(type, key, options) : + (isTypeRef && isKeyRef) ? Computed('Omit', [type, typeKey], options) : + (!isTypeRef && isKeyRef) ? Computed('Omit', [type, typeKey], options) : + (isTypeRef && !isKeyRef) ? Computed('Omit', [type, typeKey], options) : + CreateType({ ...OmitResolve(type, propertyKeys), ...options }) + ) as never +} \ No newline at end of file diff --git a/src/type/partial/partial.ts b/src/type/partial/partial.ts index 21bd5cf3e..5fbc95a7a 100644 --- a/src/type/partial/partial.ts +++ b/src/type/partial/partial.ts @@ -31,85 +31,114 @@ import type { TSchema, SchemaOptions } from '../schema/index' import type { Evaluate, Ensure } from '../helpers/index' import type { TMappedResult } from '../mapped/index' import { type TReadonlyOptional } from '../readonly-optional/index' +import { type TComputed, Computed } from '../computed/index' import { type TOptional, Optional } from '../optional/index' import { type TReadonly } from '../readonly/index' import { type TRecursive } from '../recursive/index' import { type TObject, type TProperties, Object } from '../object/index' import { type TIntersect, Intersect } from '../intersect/index' import { type TUnion, Union } from '../union/index' +import { type TRef, Ref } from '../ref/index' import { Discard } from '../discard/index' import { TransformKind } from '../symbols/index' - import { PartialFromMappedResult, type TPartialFromMappedResult } from './partial-from-mapped-result' // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -import { IsMappedResult, IsIntersect, IsUnion, IsObject } from '../guard/kind' +import { IsMappedResult, IsIntersect, IsUnion, IsObject, IsRef, IsComputed } from '../guard/kind' + // ------------------------------------------------------------------ -// FromRest +// FromComputed // ------------------------------------------------------------------ // prettier-ignore -type TFromRest = ( - T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? TFromRest]> - : Acc -) +type TFromComputed = Ensure< + TComputed<'Partial', [TComputed]> +> +// prettier-ignore +function FromComputed(target: Target, parameters: Parameters): TFromComputed { + return Computed('Partial', [Computed(target, parameters)]) as never +} +// ------------------------------------------------------------------ +// FromRef +// ------------------------------------------------------------------ +// prettier-ignore +type TFromRef = Ensure< + TComputed<'Partial', [TRef]> +> // prettier-ignore -function FromRest(T: [...T]): TFromRest { - return T.map(L => PartialResolve(L)) as never +function FromRef($ref: Ref): TFromRef { + return Computed('Partial', [Ref($ref)]) as never } // ------------------------------------------------------------------ // FromProperties // ------------------------------------------------------------------ // prettier-ignore -type TFromProperties = Evaluate<{ - [K in keyof T]: - T[K] extends (TReadonlyOptional) ? TReadonlyOptional : - T[K] extends (TReadonly) ? TReadonlyOptional : - T[K] extends (TOptional) ? TOptional : - TOptional +type TFromProperties = Evaluate<{ + [K in keyof Properties]: + Properties[K] extends (TReadonlyOptional) ? TReadonlyOptional : + Properties[K] extends (TReadonly) ? TReadonlyOptional : + Properties[K] extends (TOptional) ? TOptional : + TOptional }> // prettier-ignore -function FromProperties(T: T): TFromProperties { - const Acc = {} as TProperties - for(const K of globalThis.Object.getOwnPropertyNames(T)) Acc[K] = Optional(T[K]) - return Acc as never +function FromProperties(T: Properties): TFromProperties { + const properties = {} as TProperties + for(const K of globalThis.Object.getOwnPropertyNames(T)) properties[K] = Optional(T[K]) + return properties as never } // ------------------------------------------------------------------ // FromObject // ------------------------------------------------------------------ // prettier-ignore -type TFromObject = Ensure = Ensure )>> // prettier-ignore -function FromObject(T: T): TFromObject { +function FromObject(T: Type): TFromObject { const options = Discard(T, [TransformKind, '$id', 'required', 'properties']) const properties = FromProperties(T['properties']) return Object(properties, options) as never } + // ------------------------------------------------------------------ // PartialResolve // ------------------------------------------------------------------ // prettier-ignore -function PartialResolve(T: T): TPartial { +function PartialResolve(type: Type): TPartial { return ( - IsIntersect(T) ? Intersect(FromRest(T.allOf)) : - IsUnion(T) ? Union(FromRest(T.anyOf)) : - IsObject(T) ? FromObject(T) : + IsComputed(type) ? FromComputed(type.target, type.parameters) : + IsRef(type) ? FromRef(type.$ref) : + IsIntersect(type) ? Intersect(FromRest(type.allOf)) : + IsUnion(type) ? Union(FromRest(type.anyOf)) : + IsObject(type) ? FromObject(type) : Object({}) ) as never } // ------------------------------------------------------------------ +// FromRest +// ------------------------------------------------------------------ +// prettier-ignore +type TFromRest = ( + Types extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? TFromRest]> + : Result +) +// prettier-ignore +function FromRest(types: [...Types]): TFromRest { + return types.map(L => PartialResolve(L)) as never +} +// ------------------------------------------------------------------ // TPartial // ------------------------------------------------------------------ // prettier-ignore export type TPartial = ( - T extends TRecursive ? TRecursive> : - T extends TIntersect ? TIntersect> : - T extends TUnion ? TUnion> : - T extends TObject ? TFromObject> : + T extends TRecursive ? TRecursive> : + T extends TComputed ? TFromComputed : + T extends TIntersect ? TIntersect> : + T extends TUnion ? TUnion> : + T extends TObject ? TFromObject> : + T extends TRef ? TFromRef : TObject<{}> ) /** `[Json]` Constructs a type where all properties are optional */ diff --git a/src/type/pick/pick-from-mapped-key.ts b/src/type/pick/pick-from-mapped-key.ts index 062e1e527..86933aa58 100644 --- a/src/type/pick/pick-from-mapped-key.ts +++ b/src/type/pick/pick-from-mapped-key.ts @@ -36,77 +36,54 @@ import { Clone } from '../clone/value' // FromPropertyKey // ------------------------------------------------------------------ // prettier-ignore -type TFromPropertyKey< - T extends TSchema, - K extends PropertyKey, -> = { - [_ in K]: TPick - } +type TFromPropertyKey = { + [_ in Key]: TPick +} // prettier-ignore -function FromPropertyKey< - T extends TSchema, - K extends PropertyKey, ->(T: T, K: K, options?: SchemaOptions): TFromPropertyKey { +function FromPropertyKey(type: Type, key: Key, options?: SchemaOptions): TFromPropertyKey { return { - [K]: Pick(T, [K], Clone(options)) + [key]: Pick(type, [key], Clone(options)) } as never } // ------------------------------------------------------------------ // FromPropertyKeys // ------------------------------------------------------------------ // prettier-ignore -type TFromPropertyKeys< - T extends TSchema, - K extends PropertyKey[], - Acc extends TProperties = {} -> = ( - K extends [infer LK extends PropertyKey, ...infer RK extends PropertyKey[]] - ? TFromPropertyKeys> - : Acc +type TFromPropertyKeys = ( + PropertyKeys extends [infer LeftKey extends PropertyKey, ...infer RightKeys extends PropertyKey[]] + ? TFromPropertyKeys> + : Result ) // prettier-ignore -function FromPropertyKeys< - T extends TSchema, - K extends PropertyKey[] ->(T: T, K: [...K], options?: SchemaOptions): TFromPropertyKeys { - return K.reduce((Acc, LK) => { - return { ...Acc, ...FromPropertyKey(T, LK, options) } +function FromPropertyKeys(type: Type, propertyKeys: [...PropertyKeys], options?: SchemaOptions): TFromPropertyKeys { + return propertyKeys.reduce((result, leftKey) => { + return { ...result, ...FromPropertyKey(type, leftKey, options) } }, {} as TProperties) as never } // ------------------------------------------------------------------ // FromMappedKey // ------------------------------------------------------------------ // prettier-ignore -type TFromMappedKey< - T extends TSchema, - K extends TMappedKey, -> = ( - TFromPropertyKeys +type TFromMappedKey = ( + TFromPropertyKeys ) // prettier-ignore -function FromMappedKey< - T extends TSchema, - K extends TMappedKey, ->(T: T, K: K, options?: SchemaOptions): TFromMappedKey { - return FromPropertyKeys(T, K.keys, options) as never +function FromMappedKey(type: Type, mappedKey: MappedKey, options?: SchemaOptions): TFromMappedKey { + return FromPropertyKeys(type, mappedKey.keys, options) as never } // ------------------------------------------------------------------ // PickFromMappedKey // ------------------------------------------------------------------ // prettier-ignore -export type TPickFromMappedKey< - T extends TSchema, - K extends TMappedKey, - P extends TProperties = TFromMappedKey +export type TPickFromMappedKey > = ( - TMappedResult

+ TMappedResult ) // prettier-ignore -export function PickFromMappedKey< - T extends TSchema, - K extends TMappedKey, - P extends TProperties = TFromMappedKey ->(T: T, K: K, options?: SchemaOptions): TMappedResult

{ - const P = FromMappedKey(T, K, options) - return MappedResult(P) as never +export function PickFromMappedKey +>(type: Type, mappedKey: MappedKey, options?: SchemaOptions): TMappedResult { + const properties = FromMappedKey(type, mappedKey, options) + return MappedResult(properties) as never } diff --git a/src/type/pick/pick-from-mapped-result.ts b/src/type/pick/pick-from-mapped-result.ts index 3abf5ad17..7fa74f9c4 100644 --- a/src/type/pick/pick-from-mapped-result.ts +++ b/src/type/pick/pick-from-mapped-result.ts @@ -37,55 +37,41 @@ import { Clone } from '../clone/value' // FromProperties // ------------------------------------------------------------------ // prettier-ignore -type TFromProperties< - P extends TProperties, - K extends PropertyKey[], -> = ( - { [K2 in keyof P]: TPick } +type TFromProperties = ( + { [K2 in keyof Properties]: TPick } ) // prettier-ignore -function FromProperties< - P extends TProperties, - K extends PropertyKey[], ->(P: P, K: [...K], options?: SchemaOptions): TFromProperties { - const Acc = {} as TProperties - for(const K2 of globalThis.Object.getOwnPropertyNames(P)) Acc[K2] = Pick(P[K2], K, Clone(options)) - return Acc as never +function FromProperties(properties: Properties, propertyKeys: [...PropertyKeys], options?: SchemaOptions): TFromProperties { + const result = {} as TProperties + for(const K2 of globalThis.Object.getOwnPropertyNames(properties)) result[K2] = Pick(properties[K2], propertyKeys, Clone(options)) + return result as never } // ------------------------------------------------------------------ // FromMappedResult // ------------------------------------------------------------------ // prettier-ignore -type TFromMappedResult< - R extends TMappedResult, - K extends PropertyKey[], -> = ( - Evaluate> +type TFromMappedResult = ( + Evaluate> ) // prettier-ignore -function FromMappedResult< - R extends TMappedResult, - K extends PropertyKey[] ->(R: R, K: [...K], options?: SchemaOptions): TFromMappedResult { - return FromProperties(R.properties, K, options) as never +function FromMappedResult(mappedResult: MappedResult, propertyKeys: [...PropertyKeys], options?: SchemaOptions): TFromMappedResult { + return FromProperties(mappedResult.properties, propertyKeys, options) as never } // ------------------------------------------------------------------ // PickFromMappedResult // ------------------------------------------------------------------ // prettier-ignore -export type TPickFromMappedResult< - T extends TMappedResult, - K extends PropertyKey[], - P extends TProperties = TFromMappedResult +export type TPickFromMappedResult > = ( - Ensure> + Ensure> ) // prettier-ignore export function PickFromMappedResult< - R extends TMappedResult, - K extends PropertyKey[], - P extends TProperties = TFromMappedResult ->(R: R, K: [...K], options?: SchemaOptions): TMappedResult

{ - const P = FromMappedResult(R, K, options) - return MappedResult(P) as never + MappedResult extends TMappedResult, + PropertyKeys extends PropertyKey[], + Properties extends TProperties = TFromMappedResult +>(mappedResult: MappedResult, propertyKeys: [...PropertyKeys], options?: SchemaOptions): TMappedResult { + const properties = FromMappedResult(mappedResult, propertyKeys, options) + return MappedResult(properties) as never } diff --git a/src/type/pick/pick.ts b/src/type/pick/pick.ts index bde1e17f0..ab26dcf37 100644 --- a/src/type/pick/pick.ts +++ b/src/type/pick/pick.ts @@ -31,59 +31,70 @@ import { Discard } from '../discard/discard' import type { TSchema, SchemaOptions } from '../schema/index' import type { TupleToUnion, Evaluate, Ensure } from '../helpers/index' import { type TRecursive } from '../recursive/index' -import { type TIntersect, Intersect } from '../intersect/index' -import { type TUnion, Union } from '../union/index' -import { type TObject, type TProperties, type TPropertyKey, Object } from '../object/index' -import type { TMappedKey, TMappedResult } from '../mapped/index' +import { Computed, type TComputed } from '../computed/index' +import { Intersect, type TIntersect } from '../intersect/index' +import { Literal, type TLiteral, type TLiteralValue } from '../literal/index' +import { Object, type TObject, type TProperties, type TPropertyKey } from '../object/index' +import { Union, type TUnion } from '../union/index' +import { type TMappedKey, type TMappedResult } from '../mapped/index' +import { type TRef } from '../ref/index' import { IndexPropertyKeys, type TIndexPropertyKeys } from '../indexed/index' -import { PickFromMappedKey, type TPickFromMappedKey } from './pick-from-mapped-key' -import { PickFromMappedResult, type TPickFromMappedResult } from './pick-from-mapped-result' import { TransformKind } from '../symbols/symbols' // ------------------------------------------------------------------ -// TypeGuard +// Guards +// ------------------------------------------------------------------ +import { IsMappedKey, IsMappedResult, IsIntersect, IsUnion, IsObject, IsSchema, IsLiteralValue, IsRef } from '../guard/kind' +import { IsArray as IsArrayValue } from 'src/value/guard' + +// ------------------------------------------------------------------ +// Infrastructure // ------------------------------------------------------------------ -import { IsMappedKey, IsMappedResult, IsIntersect, IsUnion, IsObject, IsSchema } from '../guard/kind' +import { PickFromMappedKey, type TPickFromMappedKey } from './pick-from-mapped-key' +import { PickFromMappedResult, type TPickFromMappedResult } from './pick-from-mapped-result' + // ------------------------------------------------------------------ // FromIntersect // ------------------------------------------------------------------ // prettier-ignore -type FromIntersect = - T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? FromIntersect]> - : Acc -function FromIntersect(T: T, K: K) { - return T.map((T) => PickResolve(T, K)) as FromIntersect +type TFromIntersect = + Types extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? TFromIntersect]> + : Result +function FromIntersect(types: Types, propertyKeys: PropertyKeys): TFromIntersect { + return types.map((type) => PickResolve(type, propertyKeys)) as never } // ------------------------------------------------------------------ // FromUnion // ------------------------------------------------------------------ // prettier-ignore -type FromUnion = - T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? FromUnion]> - : Acc +type TFromUnion = + Types extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? TFromUnion]> + : Result // prettier-ignore -function FromUnion(T: T, K: K) { - return T.map((T) => PickResolve(T, K)) as FromUnion +function FromUnion(types: Types, propertyKeys: PropertyKeys): TFromUnion { + return types.map((type) => PickResolve(type, propertyKeys)) as never } // ------------------------------------------------------------------ // FromProperties // ------------------------------------------------------------------ // prettier-ignore -type TFromProperties> = Evaluate> +type TFromProperties> = ( + Evaluate> +) // prettier-ignore -function FromProperties(T: T, K: K): TFromProperties { - const Acc = {} as TProperties - for(const K2 of K) if(K2 in T) Acc[K2 as TPropertyKey] = T[K2 as keyof T] - return Acc as never +function FromProperties(properties: Properties, propertyKeys: PropertyKeys): TFromProperties { + const result = {} as TProperties + for(const K2 of propertyKeys) if(K2 in properties) result[K2 as TPropertyKey] = properties[K2 as keyof Properties] + return result as never } // ------------------------------------------------------------------ // FromObject // ------------------------------------------------------------------ // prettier-ignore -type TFromObject = Ensure +type TFromObject = Ensure )>> // prettier-ignore function FromObject(T: T, K: K): TFromObject { @@ -92,39 +103,84 @@ function FromObject(T: T, K: K): TFr return Object(properties, options) as never } // ------------------------------------------------------------------ -// PickResolve +// UnionFromPropertyKeys // ------------------------------------------------------------------ // prettier-ignore -function PickResolve(T: T, K: [...K]): TPick { +type TUnionFromPropertyKeys = ( + PropertyKeys extends [infer Key extends PropertyKey, ...infer Rest extends PropertyKey[]] + ? Key extends TLiteralValue + ? TUnionFromPropertyKeys]> + : TUnionFromPropertyKeys + : TUnion +) +// prettier-ignore +function UnionFromPropertyKeys(propertyKeys: PropertyKeys): TUnionFromPropertyKeys { + const result = propertyKeys.reduce((result, key) => IsLiteralValue(key) ? [...result, Literal(key)]: result, [] as TLiteral[]) + return Union(result) as never +} +// ------------------------------------------------------------------ +// TPickResolve +// ------------------------------------------------------------------ +// prettier-ignore +export type TPickResolve = ( + Properties extends TRecursive ? TRecursive> : + Properties extends TIntersect ? TIntersect> : + Properties extends TUnion ? TUnion> : + Properties extends TObject ? TFromObject, PropertyKeys> : + TObject<{}> +) +// prettier-ignore +function PickResolve(properties: Properties, propertyKeys: [...PropertyKeys]): TPickResolve { return ( - IsIntersect(T) ? Intersect(FromIntersect(T.allOf, K)) : - IsUnion(T) ? Union(FromUnion(T.anyOf, K)) : - IsObject(T) ? FromObject(T, K) : + IsIntersect(properties) ? Intersect(FromIntersect(properties.allOf, propertyKeys)) : + IsUnion(properties) ? Union(FromUnion(properties.anyOf, propertyKeys)) : + IsObject(properties) ? FromObject(properties, propertyKeys) : Object({}) ) as never } +// ------------------------------------------------------------------ +// TPick +// +// This mapping logic is to overly complex because of the decision +// to use PropertyKey[] as the default selector. The PropertyKey[] +// did make TMapped types simpler to implement, but a non-TSchema +// selector makes supporting TComputed awkward as it requires +// generalization via TSchema. This type should be reimplemented +// in the next major revision to support TSchema as the primary +// selector. +// +// ------------------------------------------------------------------ // prettier-ignore -export type TPick = - T extends TRecursive ? TRecursive> : - T extends TIntersect ? TIntersect> : - T extends TUnion ? TUnion> : - T extends TObject ? TFromObject, K> : - TObject<{}> - -/** `[Json]` Constructs a type whose keys are picked from the given type */ -export function Pick(T: T, K: [...K], options?: SchemaOptions): TPickFromMappedResult +export type TPick : Key, + PropertyKeys extends PropertyKey[] = Key extends TSchema ? TIndexPropertyKeys : Key, + IsTypeRef extends boolean = Type extends TRef ? true : false, + IsKeyRef extends boolean = Key extends TRef ? true : false, +> = ( + Type extends TMappedResult ? TPickFromMappedResult : + Key extends TMappedKey ? TPickFromMappedKey : + [IsTypeRef, IsKeyRef] extends [true, true] ? TComputed<'Pick', [Type, TypeKey]> : + [IsTypeRef, IsKeyRef] extends [false, true] ? TComputed<'Pick', [Type, TypeKey]> : + [IsTypeRef, IsKeyRef] extends [true, false] ? TComputed<'Pick', [Type, TypeKey]> : + TPickResolve +) /** `[Json]` Constructs a type whose keys are picked from the given type */ -export function Pick(T: T, K: K, options?: SchemaOptions): TPickFromMappedKey +export function Pick(type: Type, key: readonly [...Key], options?: SchemaOptions): TPick /** `[Json]` Constructs a type whose keys are picked from the given type */ -export function Pick>(T: T, K: K, options?: SchemaOptions): TPick +export function Pick(type: Type, key: Key, options?: SchemaOptions): TPick /** `[Json]` Constructs a type whose keys are picked from the given type */ -export function Pick(T: T, K: readonly [...K], options?: SchemaOptions): TPick -export function Pick(T: TSchema, K: any, options?: SchemaOptions): any { - // mapped - if (IsMappedKey(K)) return PickFromMappedKey(T, K, options) - if (IsMappedResult(T)) return PickFromMappedResult(T, K, options) - // non-mapped - const I = IsSchema(K) ? IndexPropertyKeys(K) : (K as string[]) - // special: mapping types require overridable options - return CreateType({ ...PickResolve(T, I), ...options }) +// prettier-ignore +export function Pick(type: any, key: any, options?: SchemaOptions): any { + const typeKey: TSchema = IsArrayValue(key) ? UnionFromPropertyKeys(key as PropertyKey[]) : key + const propertyKeys: PropertyKey[] = IsSchema(key) ? IndexPropertyKeys(key) : key + const isTypeRef: boolean = IsRef(type) + const isKeyRef: boolean = IsRef(key) + return ( + IsMappedResult(type) ? PickFromMappedResult(type, propertyKeys, options) : + IsMappedKey(key) ? PickFromMappedKey(type, key, options) : + (isTypeRef && isKeyRef) ? Computed('Pick', [type, typeKey], options) : + (!isTypeRef && isKeyRef) ? Computed('Pick', [type, typeKey], options) : + (isTypeRef && !isKeyRef) ? Computed('Pick', [type, typeKey], options) : + CreateType({ ...PickResolve(type, propertyKeys), ...options }) + ) as never } diff --git a/src/type/required/index.ts b/src/type/required/index.ts index e6d6901ff..04146ef58 100644 --- a/src/type/required/index.ts +++ b/src/type/required/index.ts @@ -27,4 +27,5 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ export * from './required-from-mapped-result' +export * from './required-ref' export * from './required' diff --git a/src/type/required/required-ref.ts b/src/type/required/required-ref.ts new file mode 100644 index 000000000..e606b7a42 --- /dev/null +++ b/src/type/required/required-ref.ts @@ -0,0 +1,41 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2024 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 { Kind } from '../symbols/symbols' +import { TSchema, SchemaOptions } from '../schema/schema' +import { CreateType } from '../create/index' + +export interface TRequiredRef extends TSchema { + [Kind]: 'RequiredRef' + static: unknown + $ref: Ref +} +/** Creates a RequiredRef */ +export function RequiredRef($ref: Ref, options?: SchemaOptions): TRequiredRef { + return CreateType({ [Kind]: 'RequiredRef', $ref }, options) as never +} diff --git a/src/type/required/required.ts b/src/type/required/required.ts index fab0632f2..1fefe7b46 100644 --- a/src/type/required/required.ts +++ b/src/type/required/required.ts @@ -34,70 +34,80 @@ import { type TReadonlyOptional } from '../readonly-optional/index' import { type TOptional } from '../optional/index' import { type TReadonly } from '../readonly/index' import { type TRecursive } from '../recursive/index' +import { type TObject, type TProperties, Object } from '../object/index' import { type TIntersect, Intersect } from '../intersect/index' import { type TUnion, Union } from '../union/index' -import { type TObject, type TProperties, Object } from '../object/index' +import { type TRef } from '../ref/index' import { OptionalKind, TransformKind } from '../symbols/index' import { Discard } from '../discard/index' import { RequiredFromMappedResult, type TRequiredFromMappedResult } from './required-from-mapped-result' +import { RequiredRef, type TRequiredRef } from './required-ref' // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -import { IsMappedResult, IsIntersect, IsUnion, IsObject } from '../guard/kind' +import { IsMappedResult, IsIntersect, IsUnion, IsObject, IsRef } from '../guard/kind' // ------------------------------------------------------------------ // FromRest // ------------------------------------------------------------------ // prettier-ignore -type TFromRest = ( - T extends [infer L extends TSchema, ...infer R extends TSchema[]] +type TFromRest = ( + Types extends [infer L extends TSchema, ...infer R extends TSchema[]] ? TFromRest]> : Acc ) // prettier-ignore -function FromRest(T: [...T]) : TFromRest { +function FromRest(T: [...Types]) : TFromRest { return T.map(L => RequiredResolve(L)) as never } // ------------------------------------------------------------------ // FromProperties // ------------------------------------------------------------------ // prettier-ignore -type TFromProperties = Evaluate<{ - [K in keyof T]: - T[K] extends (TReadonlyOptional) ? TReadonly : - T[K] extends (TReadonly) ? TReadonly : - T[K] extends (TOptional) ? S : - T[K] +type TFromProperties = Evaluate<{ + [K in keyof Properties]: + Properties[K] extends (TReadonlyOptional) ? TReadonly : + Properties[K] extends (TReadonly) ? TReadonly : + Properties[K] extends (TOptional) ? S : + Properties[K] }> // prettier-ignore -function FromProperties(T: T) { - const Acc = {} as TProperties - for(const K of globalThis.Object.getOwnPropertyNames(T)) Acc[K] = Discard(T[K], [OptionalKind]) as TSchema - return Acc as never +function FromProperties(T: Properties) { + const properties = {} as TProperties + for(const K of globalThis.Object.getOwnPropertyNames(T)) properties[K] = Discard(T[K], [OptionalKind]) as TSchema + return properties as never } // ------------------------------------------------------------------ // FromObject // ------------------------------------------------------------------ // prettier-ignore -type TFromObject = Ensure = Ensure )>> // prettier-ignore -function FromObject(T: T): TFromObject { +function FromObject(T: Type): TFromObject { const options = Discard(T, [TransformKind, '$id', 'required', 'properties']) const properties = FromProperties(T['properties']) return Object(properties, options) as never } // ------------------------------------------------------------------ -// RequiredResolve +// FromRef // ------------------------------------------------------------------ +type TFromRef = Ensure> +function FromRef($ref: Ref): TFromRef { + return RequiredRef($ref) as never +} +// ------------------------------------------------------------------ +// RequiredResolve +// ------------------------------------------------------------------ // prettier-ignore function RequiredResolve(T: T): TRequired { return ( IsIntersect(T) ? Intersect(FromRest(T.allOf)) : IsUnion(T) ? Union(FromRest(T.anyOf)) : IsObject(T) ? FromObject(T) : + IsRef(T) ? FromRef(T.$ref) : Object({}) ) as never } @@ -110,6 +120,7 @@ export type TRequired = ( T extends TIntersect ? TIntersect> : T extends TUnion ? TUnion> : T extends TObject ? TFromObject> : + T extends TRef ? TFromRef : TObject<{}> ) /** `[Json]` Constructs a type where all properties are required */ diff --git a/src/type/type/json.ts b/src/type/type/json.ts index 7613dc3f4..5ff7073f0 100644 --- a/src/type/type/json.ts +++ b/src/type/type/json.ts @@ -39,7 +39,7 @@ import { Index, TIndex, type TIndexPropertyKeys, type TIndexFromMappedKey, type import { Integer, type IntegerOptions, type TInteger } from '../integer/index' import { Intersect, type IntersectOptions } from '../intersect/index' import { Capitalize, Uncapitalize, Lowercase, Uppercase, type TCapitalize, type TUncapitalize, type TLowercase, type TUppercase } from '../intrinsic/index' -import { KeyOf, type TKeyOf, type TKeyOfFromMappedResult } from '../keyof/index' +import { KeyOf, type TKeyOf } from '../keyof/index' import { Literal, type TLiteral, type TLiteralValue } from '../literal/index' import { Mapped, type TMappedFunction, type TMapped, type TMappedResult } from '../mapped/index' import { Never, type TNever } from '../never/index' @@ -49,10 +49,10 @@ import { type TMappedKey } from '../mapped/index' import { Module, TModule } from '../module/index' import { Number, type TNumber, type NumberOptions } from '../number/index' import { Object, type TObject, type TProperties, type ObjectOptions } from '../object/index' -import { Omit, type TOmit, type TOmitFromMappedKey, type TOmitFromMappedResult } from '../omit/index' +import { Omit, type TOmit } from '../omit/index' import { Optional, type TOptionalWithFlag, type TOptionalFromMappedResult } from '../optional/index' import { Partial, type TPartial, type TPartialFromMappedResult } from '../partial/index' -import { Pick, type TPick, type TPickFromMappedKey, type TPickFromMappedResult } from '../pick/index' +import { Pick, type TPick } from '../pick/index' import { Readonly, type TReadonlyWithFlag, type TReadonlyFromMappedResult } from '../readonly/index' import { ReadonlyOptional, type TReadonlyOptional } from '../readonly-optional/index' import { Record, type TRecordOrObject } from '../record/index' @@ -184,12 +184,8 @@ export class JsonTypeBuilder { return Intersect(T, options) } /** `[Json]` Creates a KeyOf type */ - public KeyOf(schema: T, options?: SchemaOptions): TKeyOfFromMappedResult - /** `[Json]` Creates a KeyOf type */ - public KeyOf(schema: T, options?: SchemaOptions): TKeyOf - /** `[Json]` Creates a KeyOf type */ - public KeyOf(schema: TSchema, options?: SchemaOptions): any { - return KeyOf(schema, options) + public KeyOf(schema: Type, options?: SchemaOptions): TKeyOf { + return KeyOf(schema, options) as never } /** `[Json]` Creates a Literal type */ public Literal(value: T, options?: SchemaOptions): TLiteral { @@ -231,17 +227,13 @@ export class JsonTypeBuilder { public Object(properties: T, options?: ObjectOptions): TObject { return Object(properties, options) } + /** `[Json]` Constructs a type whose keys are picked from the given type */ + public Omit(type: Type, key: readonly [...Key], options?: SchemaOptions): TOmit + /** `[Json]` Constructs a type whose keys are picked from the given type */ + public Omit(type: Type, key: Key, options?: SchemaOptions): TOmit /** `[Json]` Constructs a type whose keys are omitted from the given type */ - public Omit(T: T, K: [...K], options?: SchemaOptions): TOmitFromMappedResult - /** `[Json]` Constructs a type whose keys are omitted from the given type */ - public Omit(T: T, K: K, options?: SchemaOptions): TOmitFromMappedKey - /** `[Json]` Constructs a type whose keys are omitted from the given type */ - public Omit>(T: T, K: K, options?: SchemaOptions): TOmit - /** `[Json]` Constructs a type whose keys are omitted from the given type */ - public Omit(T: T, K: readonly [...K], options?: SchemaOptions): TOmit - /** `[Json]` Constructs a type whose keys are omitted from the given type */ - public Omit(schema: TSchema, unresolved: any, options?: SchemaOptions): any { - return Omit(schema, unresolved, options) + public Omit(schema: TSchema, selector: any, options?: SchemaOptions): any { + return Omit(schema, selector, options) } /** `[Json]` Constructs a type where all properties are optional */ public Partial(T: T, options?: SchemaOptions): TPartialFromMappedResult @@ -252,16 +244,12 @@ export class JsonTypeBuilder { return Partial(schema, options) } /** `[Json]` Constructs a type whose keys are picked from the given type */ - public Pick(T: T, K: [...K], options?: SchemaOptions): TPickFromMappedResult - /** `[Json]` Constructs a type whose keys are picked from the given type */ - public Pick(T: T, K: K, options?: SchemaOptions): TPickFromMappedKey - /** `[Json]` Constructs a type whose keys are picked from the given type */ - public Pick>(T: T, K: K, options?: SchemaOptions): TPick + public Pick(type: Type, key: readonly [...Key], options?: SchemaOptions): TPick /** `[Json]` Constructs a type whose keys are picked from the given type */ - public Pick(T: T, K: readonly [...K], options?: SchemaOptions): TPick + public Pick(type: Type, key: Key, options?: SchemaOptions): TPick /** `[Json]` Constructs a type whose keys are picked from the given type */ - public Pick(schema: TSchema, unresolved: any, options?: SchemaOptions): any { - return Pick(schema, unresolved, options) + public Pick(type: any, key: any, options?: SchemaOptions): any { + return Pick(type, key, options) } /** `[Json]` Creates a Record type */ public Record(key: K, schema: T, options?: ObjectOptions): TRecordOrObject { diff --git a/src/value/cast/cast.ts b/src/value/cast/cast.ts index a6f9d9891..bdcb7b8c0 100644 --- a/src/value/cast/cast.ts +++ b/src/value/cast/cast.ts @@ -136,7 +136,7 @@ function FromConstructor(schema: TConstructor, references: TSchema[], value: any } function FromImport(schema: TImport, references: TSchema[], value: unknown): boolean { const definitions = globalThis.Object.values(schema.$defs) as TSchema[] - const target = schema.$defs[schema.$ref] as TSchema + const target = schema.$defs[schema.$ref as never] as TSchema return Visit(target, [...references, ...definitions], value) } function FromIntersect(schema: TIntersect, references: TSchema[], value: any): any { diff --git a/src/value/check/check.ts b/src/value/check/check.ts index b1af0dd26..7ac4e1e82 100644 --- a/src/value/check/check.ts +++ b/src/value/check/check.ts @@ -75,9 +75,9 @@ import type { TVoid } from '../../type/void/index' // ------------------------------------------------------------------ import { IsArray, IsUint8Array, IsDate, IsPromise, IsFunction, IsAsyncIterator, IsIterator, IsBoolean, IsNumber, IsBigInt, IsString, IsSymbol, IsInteger, IsNull, IsUndefined } from '../guard/index' // ------------------------------------------------------------------ -// TypeGuard +// KindGuard // ------------------------------------------------------------------ -import { IsSchema } from '../../type/guard/type' +import { IsSchema } from '../../type/guard/kind' // ------------------------------------------------------------------ // Errors // ------------------------------------------------------------------ @@ -188,7 +188,7 @@ function FromFunction(schema: TFunction, references: TSchema[], value: any): boo } function FromImport(schema: TImport, references: TSchema[], value: any): boolean { const definitions = globalThis.Object.values(schema.$defs) as TSchema[] - const target = schema.$defs[schema.$ref] as TSchema + const target = schema.$defs[schema.$ref as never] as TSchema return Visit(target, [...references, ...definitions], value) } function FromInteger(schema: TInteger, references: TSchema[], value: any): boolean { diff --git a/src/value/clean/clean.ts b/src/value/clean/clean.ts index 341cf025e..1e7df9f3b 100644 --- a/src/value/clean/clean.ts +++ b/src/value/clean/clean.ts @@ -77,7 +77,7 @@ function FromArray(schema: TArray, references: TSchema[], value: unknown): any { } function FromImport(schema: TImport, references: TSchema[], value: unknown): any { const definitions = globalThis.Object.values(schema.$defs) as TSchema[] - const target = schema.$defs[schema.$ref] as TSchema + const target = schema.$defs[schema.$ref as never] as TSchema return Visit(target, [...references, ...definitions], value) } function FromIntersect(schema: TIntersect, references: TSchema[], value: unknown): any { diff --git a/src/value/convert/convert.ts b/src/value/convert/convert.ts index 1cfaa1081..bacae2bad 100644 --- a/src/value/convert/convert.ts +++ b/src/value/convert/convert.ts @@ -185,7 +185,7 @@ function FromDate(schema: TDate, references: TSchema[], value: any): unknown { } function FromImport(schema: TImport, references: TSchema[], value: unknown): unknown { const definitions = globalThis.Object.values(schema.$defs) as TSchema[] - const target = schema.$defs[schema.$ref] as TSchema + const target = schema.$defs[schema.$ref as never] as TSchema return Visit(target, [...references, ...definitions], value) } function FromInteger(schema: TInteger, references: TSchema[], value: any): unknown { diff --git a/src/value/create/create.ts b/src/value/create/create.ts index 478aab130..00a0c0c17 100644 --- a/src/value/create/create.ts +++ b/src/value/create/create.ts @@ -170,7 +170,7 @@ function FromFunction(schema: TFunction, references: TSchema[]): any { } function FromImport(schema: TImport, references: TSchema[]): any { const definitions = globalThis.Object.values(schema.$defs) as TSchema[] - const target = schema.$defs[schema.$ref] as TSchema + const target = schema.$defs[schema.$ref as never] as TSchema return Visit(target, [...references, ...definitions]) } function FromInteger(schema: TInteger, references: TSchema[]): any { diff --git a/src/value/default/default.ts b/src/value/default/default.ts index 30ddbc95b..3e3790478 100644 --- a/src/value/default/default.ts +++ b/src/value/default/default.ts @@ -81,7 +81,7 @@ function FromDate(schema: TArray, references: TSchema[], value: unknown): any { } function FromImport(schema: TImport, references: TSchema[], value: unknown): any { const definitions = globalThis.Object.values(schema.$defs) as TSchema[] - const target = schema.$defs[schema.$ref] as TSchema + const target = schema.$defs[schema.$ref as never] as TSchema return Visit(target, [...references, ...definitions], value) } function FromIntersect(schema: TIntersect, references: TSchema[], value: unknown): any { diff --git a/src/value/transform/decode.ts b/src/value/transform/decode.ts index 2498058b1..8bb957b09 100644 --- a/src/value/transform/decode.ts +++ b/src/value/transform/decode.ts @@ -51,9 +51,9 @@ import type { TUnion } from '../../type/union/index' // ------------------------------------------------------------------ import { HasPropertyKey, IsObject, IsArray, IsValueType, IsUndefined as IsUndefinedValue } from '../guard/index' // ------------------------------------------------------------------ -// TypeGuard +// KindGuard // ------------------------------------------------------------------ -import { IsTransform, IsSchema, IsUndefined } from '../../type/guard/type' +import { IsTransform, IsSchema, IsUndefined } from '../../type/guard/kind' // ------------------------------------------------------------------ // Errors // ------------------------------------------------------------------ @@ -119,7 +119,7 @@ function FromIntersect(schema: TIntersect, references: TSchema[], path: string, // prettier-ignore function FromImport(schema: TImport, references: TSchema[], path: string, value: unknown): unknown { const definitions = globalThis.Object.values(schema.$defs) as TSchema[] - const target = schema.$defs[schema.$ref] as TSchema + const target = schema.$defs[schema.$ref as never] as TSchema const transform = schema[TransformKind as never] // Note: we need to re-spec the target as TSchema + [TransformKind] const transformTarget = { [TransformKind]: transform, ...target } as TSchema diff --git a/src/value/transform/encode.ts b/src/value/transform/encode.ts index 1f033dc79..3be3ba671 100644 --- a/src/value/transform/encode.ts +++ b/src/value/transform/encode.ts @@ -51,9 +51,9 @@ import type { TUnion } from '../../type/union/index' // ------------------------------------------------------------------ import { HasPropertyKey, IsObject, IsArray, IsValueType, IsUndefined as IsUndefinedValue } from '../guard/index' // ------------------------------------------------------------------ -// TypeGuard +// KindGuard // ------------------------------------------------------------------ -import { IsTransform, IsSchema, IsUndefined } from '../../type/guard/type' +import { IsTransform, IsSchema, IsUndefined } from '../../type/guard/kind' // ------------------------------------------------------------------ // Errors // ------------------------------------------------------------------ @@ -99,7 +99,7 @@ function FromArray(schema: TArray, references: TSchema[], path: string, value: a // prettier-ignore function FromImport(schema: TImport, references: TSchema[], path: string, value: unknown): unknown { const definitions = globalThis.Object.values(schema.$defs) as TSchema[] - const target = schema.$defs[schema.$ref] as TSchema + const target = schema.$defs[schema.$ref as never] as TSchema const transform = schema[TransformKind as never] // Note: we need to re-spec the target as TSchema + [TransformKind] const transformTarget = { [TransformKind]: transform, ...target } as TSchema diff --git a/src/value/transform/has.ts b/src/value/transform/has.ts index 7481d86a1..5a0d785c3 100644 --- a/src/value/transform/has.ts +++ b/src/value/transform/has.ts @@ -46,9 +46,9 @@ import type { TTuple } from '../../type/tuple/index' import type { TUnion } from '../../type/union/index' // ------------------------------------------------------------------ -// TypeGuard +// KindGuard // ------------------------------------------------------------------ -import { IsTransform, IsSchema } from '../../type/guard/type' +import { IsTransform, IsSchema } from '../../type/guard/kind' // ------------------------------------------------------------------ // ValueGuard // ------------------------------------------------------------------ diff --git a/test/static/pick.ts b/test/static/pick.ts index de29ba4bb..895cdefac 100644 --- a/test/static/pick.ts +++ b/test/static/pick.ts @@ -27,7 +27,7 @@ import { Type, Static } from '@sinclair/typebox' const keys = ['A', 'B'] as const - const T = Type.Pick(A, keys) + const T = Type.Pick(A, ['A', 'B']) type T = Static