diff --git a/example/index.ts b/example/index.ts index 10a612d8..11348e92 100644 --- a/example/index.ts +++ b/example/index.ts @@ -14,50 +14,22 @@ import { Type, TypeGuard, Kind, Static, TSchema } from '@sinclair/typebox' // - Required (Computed) // - 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 T = Required> & - Partial> -}`) - -// const Module = Type.Module({ -// A: Type.Partial(Type.Object({ -// x: Type.Number(), -// y: Type.Number(), -// z: Type.Number(), -// })), -// T: Type.Intersect([ -// Type.Required(Type.Pick(Type.Ref('A'), ['x'])), -// Type.Partial(Type.Omit(Type.Ref('A'), ['x'])), -// ]) -// }) - -const AA = Type.Required(Type.Pick(Type.Ref('A'), ['x'])) - +const Module = Type.Module({ + A: Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + T: Type.KeyOf(Type.Ref('A')), +}) + +const A = Module.Import('A') const T = Module.Import('T') function test(value: Static) {} console.dir(T, { depth: 100 }) -type A = Static +type A = Static +type T = Static // Ok(T, { nodes: [{ nodes: [{ nodes: [] }, { nodes: [] }] }] }) - -const A = Type.Omit(Type.String(), Type.Ref('A')) diff --git a/readme.md b/readme.md index 3adeae92..c1669cfd 100644 --- a/readme.md +++ b/readme.md @@ -1074,22 +1074,25 @@ const T = Type.Object({ // const T: TObject<{ ### Module -Syntax Types support Module parsing, which is useful for processing multiple TypeScript types. Module parsing supports type alias and interface definitions. Generics are currently unsupported as of 0.34.0. +Syntax Types also support Module parsing. This can provide a more terse syntax for creating Module definitions, but comes with an inference performance cost. Module parsing supports interface and type alias definitions. Generics types are currently unsupported. Consider the following which defines a explicit Module on the left, and the parsed Module on the right. ```typescript -const Foo = Parse(`module Foo { - - export type A = string - - export type B = number - - export type C = A | B - -}`) - -const C = Foo.Import('C') // const C: TImport<{ - // ... - // }, 'C'> +const Module = Type.Module({ // const Module = Parse(`module { + // + User: Type.Object({ // export interface User { + id: Type.String(), // id: string + name: Type.String(), // name: string + email: Type.String(), // email: string + }), // } + // + PartialUser: Type.Intersect([ // export type PartialUser = ( + Type.Pick(Type.Ref('User'), ['id']), // Pick & + Type.Partial( // Partial> + Type.Omit(Type.Ref('User'), ['id']) // ) + ), // + ]) // + // +}) // }`) ``` diff --git a/src/type/keyof/index.ts b/src/type/keyof/index.ts index f0c1a32c..67512251 100644 --- a/src/type/keyof/index.ts +++ b/src/type/keyof/index.ts @@ -29,5 +29,4 @@ 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 f4b90bfa..0be65975 100644 --- a/src/type/keyof/keyof-from-mapped-result.ts +++ b/src/type/keyof/keyof-from-mapped-result.ts @@ -30,21 +30,17 @@ 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 TKeyOfUnion } from './keyof' +import { KeyOf, type TKeyOfFromType } from './keyof' import { Clone } from '../clone/value' // ------------------------------------------------------------------ // FromProperties // ------------------------------------------------------------------ // prettier-ignore -type TFromProperties< - Properties extends TProperties -> = ( - { [K2 in keyof Properties]: TKeyOfUnion } +type TFromProperties = ( + { [K2 in keyof Properties]: TKeyOfFromType } ) // prettier-ignore -function FromProperties< - Properties extends TProperties ->(properties: Properties, options?: SchemaOptions): TFromProperties { +function FromProperties(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 @@ -53,15 +49,11 @@ function FromProperties< // FromMappedResult // ------------------------------------------------------------------ // prettier-ignore -type TFromMappedResult< - MappedResult extends TMappedResult -> = ( +type TFromMappedResult = ( Evaluate> ) // prettier-ignore -function FromMappedResult< - MappedResult extends TMappedResult ->(mappedResult: MappedResult, options?: SchemaOptions): TFromMappedResult { +function FromMappedResult(mappedResult: MappedResult, options?: SchemaOptions): TFromMappedResult { return FromProperties(mappedResult.properties, options) as never } // ------------------------------------------------------------------ diff --git a/src/type/keyof/keyof-property-keys.ts b/src/type/keyof/keyof-property-keys.ts index 28f945bb..25cfd2df 100644 --- a/src/type/keyof/keyof-property-keys.ts +++ b/src/type/keyof/keyof-property-keys.ts @@ -44,58 +44,56 @@ import { IsIntersect, IsUnion, IsTuple, IsArray, IsObject, IsRecord } from '../g // FromRest // ------------------------------------------------------------------ // prettier-ignore -type TFromRest = ( - T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? TFromRest]> - : Acc +type TFromRest = ( + Types extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? TFromRest]> + : Result ) // prettier-ignore -function FromRest(T: [...T]): TFromRest { - const Acc = [] as PropertyKey[][] - for(const L of T) Acc.push(KeyOfPropertyKeys(L)) - return Acc as never +function FromRest(types: [...Types]): TFromRest { + const result = [] as PropertyKey[][] + for(const L of types) result.push(KeyOfPropertyKeys(L)) + return result as never } // ------------------------------------------------------------------ // FromIntersect // ------------------------------------------------------------------ // prettier-ignore -type TFromIntersect< - T extends TSchema[], - C extends PropertyKey[][] = TFromRest, - R extends PropertyKey[] = TSetUnionMany -> = R +type TFromIntersect, + PropertyKeys extends PropertyKey[] = TSetUnionMany +> = PropertyKeys // prettier-ignore -function FromIntersect(T: [...T]): TFromIntersect { - const C = FromRest(T) as PropertyKey[][] - const R = SetUnionMany(C) - return R as never +function FromIntersect(types: [...Types]): TFromIntersect { + const propertyKeysArray = FromRest(types) as PropertyKey[][] + const propertyKeys = SetUnionMany(propertyKeysArray) + return propertyKeys as never } // ------------------------------------------------------------------ // FromUnion // ------------------------------------------------------------------ // prettier-ignore -type TFromUnion< - T extends TSchema[], - C extends PropertyKey[][] = TFromRest, - R extends PropertyKey[] = TSetIntersectMany -> = R +type TFromUnion, + PropertyKeys extends PropertyKey[] = TSetIntersectMany +> = PropertyKeys // prettier-ignore -function FromUnion(T: [...T]): TFromUnion { - const C = FromRest(T) as PropertyKey[][] - const R = SetIntersectMany(C) - return R as never +function FromUnion(types: [...Types]): TFromUnion { + const propertyKeysArray = FromRest(types) as PropertyKey[][] + const propertyKeys = SetIntersectMany(propertyKeysArray) + return propertyKeys as never } // ------------------------------------------------------------------ // FromTuple // ------------------------------------------------------------------ // prettier-ignore -type TFromTuple = - T extends [infer _ extends TSchema, ...infer R extends TSchema[]] - ? TFromTuple, [...Acc, I]> +type TFromTuple = + Types extends [infer _ extends TSchema, ...infer R extends TSchema[]] + ? TFromTuple, [...Acc, Indexer]> : Acc // prettier-ignore -function FromTuple(T: [...T]): TFromTuple { - return T.map((_, I) => I.toString()) as never +function FromTuple(types: [...Types]): TFromTuple { + return types.map((_, indexer) => indexer.toString()) as never } // ------------------------------------------------------------------ // FromArray @@ -114,11 +112,11 @@ function FromArray<_ extends TSchema>(_: _): TFromArray<_> { // FromProperties // ------------------------------------------------------------------ // prettier-ignore -type TFromProperties = ( - UnionToTuple +type TFromProperties = ( + UnionToTuple ) // prettier-ignore -function FromProperties(T: T): TFromProperties { +function FromProperties(T: Properties): TFromProperties { return ( globalThis.Object.getOwnPropertyNames(T) ) as never @@ -140,25 +138,25 @@ function FromPatternProperties(patternProperties: Record): // KeyOfPropertyKeys // ------------------------------------------------------------------ // prettier-ignore -export type TKeyOfPropertyKeys = ( - T extends TRecursive ? TKeyOfPropertyKeys : - T extends TIntersect ? TFromIntersect : - T extends TUnion ? TFromUnion : - T extends TTuple ? TFromTuple : - T extends TArray ? TFromArray : - T extends TObject ? TFromProperties : +export type TKeyOfPropertyKeys = ( + Type extends TRecursive ? TKeyOfPropertyKeys : + Type extends TIntersect ? TFromIntersect : + Type extends TUnion ? TFromUnion : + Type extends TTuple ? TFromTuple : + Type extends TArray ? TFromArray : + Type extends TObject ? TFromProperties : [] ) /** Returns a tuple of PropertyKeys derived from the given TSchema. */ // prettier-ignore -export function KeyOfPropertyKeys(T: T): TKeyOfPropertyKeys { +export function KeyOfPropertyKeys(type: Type): TKeyOfPropertyKeys { return ( - IsIntersect(T) ? FromIntersect(T.allOf) : - IsUnion(T) ? FromUnion(T.anyOf) : - IsTuple(T) ? FromTuple(T.items ?? []) : - IsArray(T) ? FromArray(T.items) : - IsObject(T) ? FromProperties(T.properties) : - IsRecord(T) ? FromPatternProperties(T.patternProperties) : + IsIntersect(type) ? FromIntersect(type.allOf) : + IsUnion(type) ? FromUnion(type.anyOf) : + IsTuple(type) ? FromTuple(type.items ?? []) : + IsArray(type) ? FromArray(type.items) : + IsObject(type) ? FromProperties(type.properties) : + IsRecord(type) ? FromPatternProperties(type.patternProperties) : [] ) as never } diff --git a/src/type/keyof/keyof-ref.ts b/src/type/keyof/keyof-ref.ts deleted file mode 100644 index 47acfcee..00000000 --- a/src/type/keyof/keyof-ref.ts +++ /dev/null @@ -1,41 +0,0 @@ -/*-------------------------------------------------------------------------- - -@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 587e1e07..fbd20df6 100644 --- a/src/type/keyof/keyof.ts +++ b/src/type/keyof/keyof.ts @@ -33,16 +33,55 @@ 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 { Computed, TComputed } from '../computed/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, IsRef } from '../guard/kind' +import { IsMappedResult, IsRef, IsComputed } from '../guard/kind' +// ------------------------------------------------------------------ +// FromComputed +// ------------------------------------------------------------------ +// prettier-ignore +type TFromComputed = Ensure< + TComputed<'KeyOf', [TComputed]> +> +// prettier-ignore +function FromComputed(target: Target, parameters: Parameters): TFromComputed { + return Computed('KeyOf', [Computed(target, parameters)]) as never +} +// ------------------------------------------------------------------ +// FromRef +// ------------------------------------------------------------------ +// prettier-ignore +type TFromRef = Ensure< + TComputed<'KeyOf', [TRef]> +> +// prettier-ignore +function FromRef($ref: Ref): TFromRef { + return Computed('KeyOf', [Ref($ref)]) as never +} +// ------------------------------------------------------------------ +// KeyOfFromType +// ------------------------------------------------------------------ +// prettier-ignore +/** `[Internal]` Used by KeyOfFromMappedResult */ +export type TKeyOfFromType, + PropertyKeyTypes extends TSchema[] = TKeyOfPropertyKeysToRest, + Result = TUnionEvaluated +> = Ensure +// prettier-ignore +function KeyOfFromType(type: Type, options?: SchemaOptions): TKeyOfFromType { + const propertyKeys = KeyOfPropertyKeys(type) as PropertyKey[] + const propertyKeyTypes = KeyOfPropertyKeysToRest(propertyKeys) + const result = UnionEvaluated(propertyKeyTypes) + return CreateType(result, options) as never +} // ------------------------------------------------------------------ // FromPropertyKeys // ------------------------------------------------------------------ @@ -58,37 +97,17 @@ export type TKeyOfPropertyKeysToRest(propertyKeys: [...PropertyKeys]): TKeyOfPropertyKeysToRest { return propertyKeys.map(L => L === '[number]' ? Number() : Literal(L as TLiteralValue)) as never } -// ------------------------------------------------------------------ -// TKeyOfUnion -// ------------------------------------------------------------------ -// prettier-ignore -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 TComputed ? TFromComputed : + Type extends TRef ? TFromRef : Type extends TMappedResult ? TKeyOfFromMappedResult : - TKeyOfUnion + TKeyOfFromType ) /** `[Json]` Creates a KeyOf type */ 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(type) as PropertyKey[] - const S = KeyOfPropertyKeysToRest(K) - const U = UnionEvaluated(S) - return CreateType(U, options) as never - } + return (IsComputed(type) ? FromComputed(type.target, type.parameters) : IsRef(type) ? FromRef(type.$ref) : IsMappedResult(type) ? KeyOfFromMappedResult(type, options) : KeyOfFromType(type, options)) as never } diff --git a/src/type/module/compute.ts b/src/type/module/compute.ts index 65204035..ed4f4299 100644 --- a/src/type/module/compute.ts +++ b/src/type/module/compute.ts @@ -38,7 +38,7 @@ 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 { KeyOf, type TKeyOf } 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' @@ -102,18 +102,29 @@ function Deref(moduleP // ------------------------------------------------------------------ // prettier-ignore type TFromAwaited = ( - Parameters extends [infer T0 extends TSchema] ? TAwaited : TNever + Parameters extends [infer T0 extends TSchema] ? TAwaited : never ) // prettier-ignore function FromAwaited(parameters: Parameters): TFromAwaited { return Awaited(parameters[0]) as never } // ------------------------------------------------------------------ +// KeyOf +// ------------------------------------------------------------------ +// prettier-ignore +type TFromKeyOf = ( + Parameters extends [infer T0 extends TSchema] ? TKeyOf : never +) +// prettier-ignore +function FromKeyOf(parameters: Parameters): TFromAwaited { + return KeyOf(parameters[0]) as never +} +// ------------------------------------------------------------------ // Partial // ------------------------------------------------------------------ // prettier-ignore type TFromPartial = ( - Parameters extends [infer T0 extends TSchema] ? TPartial : TNever + Parameters extends [infer T0 extends TSchema] ? TPartial : never ) // prettier-ignore function FromPartial(parameters: Parameters): TFromPartial { @@ -124,7 +135,7 @@ function FromPartial(parameters: Parameters): TFro // ------------------------------------------------------------------ // prettier-ignore type TFromOmit = ( - Parameters extends [infer T0 extends TSchema, infer T1 extends TSchema] ? TOmit : TNever + Parameters extends [infer T0 extends TSchema, infer T1 extends TSchema] ? TOmit : never ) // prettier-ignore function FromOmit(parameters: Parameters): TFromPick { @@ -135,7 +146,7 @@ function FromOmit(parameters: Parameters): TFromPi // ------------------------------------------------------------------ // prettier-ignore type TFromPick = ( - Parameters extends [infer T0 extends TSchema, infer T1 extends TSchema] ? TPick : TNever + Parameters extends [infer T0 extends TSchema, infer T1 extends TSchema] ? TPick : never ) // prettier-ignore function FromPick(parameters: Parameters): TFromPick { @@ -146,7 +157,7 @@ function FromPick(parameters: Parameters): TFromPi // ------------------------------------------------------------------ // prettier-ignore type TFromRequired = ( - Parameters extends [infer T0 extends TSchema] ? TRequired : TNever + Parameters extends [infer T0 extends TSchema] ? TRequired : never ) // prettier-ignore function FromRequired(parameters: Parameters): TFromPick { @@ -160,6 +171,7 @@ type TFromComputed > = ( Target extends 'Awaited' ? TFromAwaited : + Target extends 'KeyOf' ? TFromKeyOf : Target extends 'Partial' ? TFromPartial : Target extends 'Omit' ? TFromOmit : Target extends 'Pick' ? TFromPick : @@ -171,6 +183,7 @@ function FromComputed(type: Type): TRequired { // TRequired // ------------------------------------------------------------------ // prettier-ignore -export type TRequired = ( - T extends TRecursive ? TRecursive> : - T extends TComputed ? TFromComputed : - T extends TRef ? TFromRef : - T extends TIntersect ? TIntersect> : - T extends TUnion ? TUnion> : - T extends TObject ? TFromObject> : +export type TRequired = ( + Type extends TRecursive ? TRecursive> : + Type extends TComputed ? TFromComputed : + Type extends TRef ? TFromRef : + Type extends TIntersect ? TIntersect> : + Type extends TUnion ? TUnion> : + Type extends TObject ? TFromObject> : TObject<{}> ) /** `[Json]` Constructs a type where all properties are required */