diff --git a/example/index.ts b/example/index.ts index c871883a..10a612d8 100644 --- a/example/index.ts +++ b/example/index.ts @@ -11,7 +11,7 @@ import { Type, TypeGuard, Kind, Static, TSchema } from '@sinclair/typebox' // - Partial (Computed) // - Pick (Computed) // - Omit (Computed) -// - Required +// - Required (Computed) // - KeyOf // @@ -25,27 +25,30 @@ import { Type, TypeGuard, Kind, Static, TSchema } from '@sinclair/typebox' 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'))), -}) +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 T = Module.Import('T') @@ -57,4 +60,4 @@ type A = Static // Ok(T, { nodes: [{ nodes: [{ nodes: [] }, { nodes: [] }] }] }) -const A = Type.Omit(Type.String(), Type.Ref('A')) \ No newline at end of file +const A = Type.Omit(Type.String(), Type.Ref('A')) diff --git a/src/syntax/static.ts b/src/syntax/static.ts index c8407ae5..ddd48ea7 100644 --- a/src/syntax/static.ts +++ b/src/syntax/static.ts @@ -776,7 +776,7 @@ type Partial = Static.Tuple<[ // prettier-ignore interface RequiredMapping extends Static.IMapping { output: this['input'] extends ['Required', LAngle, infer Type extends Types.TSchema, RAngle] - ? Types.TPartial + ? Types.TRequired : never } // prettier-ignore diff --git a/src/type/module/compute.ts b/src/type/module/compute.ts index dc48f382..65204035 100644 --- a/src/type/module/compute.ts +++ b/src/type/module/compute.ts @@ -45,7 +45,7 @@ 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 { Required, TRequired } from '../required/index' import { Tuple, type TTuple } from '../tuple/index' import { Union, type TUnion, type TUnionEvaluated } from '../union/index' @@ -54,13 +54,14 @@ import { Union, type TUnion, type TUnionEvaluated } from '../union/index' // ------------------------------------------------------------------ import * as KindGuard from '../guard/kind' - // ------------------------------------------------------------------ -// Deref +// DerefParameters +// +// Dereferences TComputed parameters. It is important to note that +// dereferencing anything other than these parameters may result +// inference and evaluation problems, potentially resulting in a +// stack overflow. All open TRef types must be preserved !!! // -// 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 = ( @@ -141,7 +142,18 @@ function FromPick(parameters: Parameters): TFromPi return Pick(parameters[0], parameters[1]) as never } // ------------------------------------------------------------------ -// ComputeComputed +// Required +// ------------------------------------------------------------------ +// prettier-ignore +type TFromRequired = ( + Parameters extends [infer T0 extends TSchema] ? TRequired : TNever +) +// prettier-ignore +function FromRequired(parameters: Parameters): TFromPick { + return Required(parameters[0]) as never +} +// ------------------------------------------------------------------ +// Computed // ------------------------------------------------------------------ // prettier-ignore type TFromComputed : Target extends 'Omit' ? TFromOmit : Target extends 'Pick' ? TFromPick : + Target extends 'Required' ? TFromRequired : TNever ) // prettier-ignore @@ -161,6 +174,7 @@ function FromComputed = Evaluate<{ TOptional }> // prettier-ignore -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 +function FromProperties(properties: Properties): TFromProperties { + const partialProperties = {} as TProperties + for(const K of globalThis.Object.getOwnPropertyNames(properties)) partialProperties[K] = Optional(properties[K]) + return partialProperties as never } // ------------------------------------------------------------------ // FromObject @@ -100,7 +100,19 @@ function FromObject(T: Type): TFromObject { const properties = FromProperties(T['properties']) return Object(properties, options) 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(type => PartialResolve(type)) as never +} // ------------------------------------------------------------------ // PartialResolve // ------------------------------------------------------------------ @@ -116,41 +128,28 @@ function PartialResolve(type: Type): TPartial { ) 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 TComputed ? TFromComputed : + T extends TRef ? TFromRef : 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 */ -export function Partial(T: T, options?: SchemaOptions): TPartialFromMappedResult +export function Partial(type: MappedResult, options?: SchemaOptions): TPartialFromMappedResult /** `[Json]` Constructs a type where all properties are optional */ -export function Partial(T: T, options?: SchemaOptions): TPartial +export function Partial(type: Type, options?: SchemaOptions): TPartial /** `[Json]` Constructs a type where all properties are optional */ -export function Partial(T: TSchema, options?: SchemaOptions): any { - if (IsMappedResult(T)) { - return PartialFromMappedResult(T, options) +export function Partial(type: TSchema, options?: SchemaOptions): any { + if (IsMappedResult(type)) { + return PartialFromMappedResult(type, options) } else { // special: mapping types require overridable options - return CreateType({ ...PartialResolve(T), ...options }) + return CreateType({ ...PartialResolve(type), ...options }) } } diff --git a/src/type/pick/pick.ts b/src/type/pick/pick.ts index ab26dcf3..9f7abcd6 100644 --- a/src/type/pick/pick.ts +++ b/src/type/pick/pick.ts @@ -141,7 +141,7 @@ function PickResolve - -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 1fefe7b4..35d2408d 100644 --- a/src/type/required/required.ts +++ b/src/type/required/required.ts @@ -31,34 +31,44 @@ 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 } 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 } from '../ref/index' +import { type TRef, Ref } 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, IsRef } from '../guard/kind' +import { IsMappedResult, IsIntersect, IsUnion, IsObject, IsRef, IsComputed } from '../guard/kind' + // ------------------------------------------------------------------ -// FromRest +// FromComputed // ------------------------------------------------------------------ // prettier-ignore -type TFromRest = ( - Types extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? TFromRest]> - : Acc -) +type TFromComputed = Ensure< + TComputed<'Required', [TComputed]> +> // prettier-ignore -function FromRest(T: [...Types]) : TFromRest { - return T.map(L => RequiredResolve(L)) as never +function FromComputed(target: Target, parameters: Parameters): TFromComputed { + return Computed('Required', [Computed(target, parameters)]) as never +} +// ------------------------------------------------------------------ +// FromRef +// ------------------------------------------------------------------ +// prettier-ignore +type TFromRef = Ensure< + TComputed<'Required', [TRef]> +> +// prettier-ignore +function FromRef($ref: Ref): TFromRef { + return Computed('Required', [Ref($ref)]) as never } // ------------------------------------------------------------------ // FromProperties @@ -72,10 +82,10 @@ type TFromProperties = Evaluate<{ Properties[K] }> // prettier-ignore -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 +function FromProperties(properties: Properties) { + const requiredProperties = {} as TProperties + for(const K of globalThis.Object.getOwnPropertyNames(properties)) requiredProperties[K] = Discard(properties[K], [OptionalKind]) as TSchema + return requiredProperties as never } // ------------------------------------------------------------------ // FromObject @@ -85,29 +95,35 @@ type TFromObject )>> // prettier-ignore -function FromObject(T: Type): TFromObject { - const options = Discard(T, [TransformKind, '$id', 'required', 'properties']) - const properties = FromProperties(T['properties']) +function FromObject(type: Type): TFromObject { + const options = Discard(type, [TransformKind, '$id', 'required', 'properties']) + const properties = FromProperties(type['properties']) return Object(properties, options) as never } // ------------------------------------------------------------------ -// FromRef +// FromRest // ------------------------------------------------------------------ -type TFromRef = Ensure> - -function FromRef($ref: Ref): TFromRef { - return RequiredRef($ref) as never +// 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(type => RequiredResolve(type)) as never } // ------------------------------------------------------------------ // RequiredResolve // ------------------------------------------------------------------ // prettier-ignore -function RequiredResolve(T: T): TRequired { +function RequiredResolve(type: Type): TRequired { return ( - IsIntersect(T) ? Intersect(FromRest(T.allOf)) : - IsUnion(T) ? Union(FromRest(T.anyOf)) : - IsObject(T) ? FromObject(T) : - IsRef(T) ? FromRef(T.$ref) : + 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 } @@ -116,23 +132,24 @@ function RequiredResolve(T: T): TRequired { // ------------------------------------------------------------------ // prettier-ignore export type TRequired = ( - T extends TRecursive ? TRecursive> : - T extends TIntersect ? TIntersect> : - T extends TUnion ? TUnion> : - T extends TObject ? TFromObject> : - T extends TRef ? TFromRef : + T extends TRecursive ? TRecursive> : + T extends TComputed ? TFromComputed : + T extends TRef ? TFromRef : + T extends TIntersect ? TIntersect> : + T extends TUnion ? TUnion> : + T extends TObject ? TFromObject> : TObject<{}> ) /** `[Json]` Constructs a type where all properties are required */ -export function Required(T: T, options?: SchemaOptions): TRequiredFromMappedResult +export function Required(mappedResult: MappedResult, options?: SchemaOptions): TRequiredFromMappedResult /** `[Json]` Constructs a type where all properties are required */ -export function Required(T: T, options?: SchemaOptions): TRequired +export function Required(type: Type, options?: SchemaOptions): TRequired /** `[Json]` Constructs a type where all properties are required */ -export function Required(T: T, options?: SchemaOptions): never { - if (IsMappedResult(T)) { - return RequiredFromMappedResult(T, options) as never +export function Required(type: Type, options?: SchemaOptions): never { + if (IsMappedResult(type)) { + return RequiredFromMappedResult(type, options) as never } else { // special: mapping types require overridable options - return CreateType({ ...RequiredResolve(T), ...options }) as never + return CreateType({ ...RequiredResolve(type), ...options }) as never } } diff --git a/src/type/type/json.ts b/src/type/type/json.ts index 5ff7073f..6a4bde30 100644 --- a/src/type/type/json.ts +++ b/src/type/type/json.ts @@ -236,12 +236,12 @@ export class JsonTypeBuilder { return Omit(schema, selector, options) } /** `[Json]` Constructs a type where all properties are optional */ - public Partial(T: T, options?: SchemaOptions): TPartialFromMappedResult + public Partial(mappedResult: MappedResult, options?: SchemaOptions): TPartialFromMappedResult /** `[Json]` Constructs a type where all properties are optional */ - public Partial(schema: T, options?: SchemaOptions): TPartial + public Partial(type: Type, options?: SchemaOptions): TPartial /** `[Json]` Constructs a type where all properties are optional */ - public Partial(schema: TSchema, options?: SchemaOptions): any { - return Partial(schema, options) + public Partial(type: TSchema, options?: SchemaOptions): any { + return Partial(type, options) } /** `[Json]` Constructs a type whose keys are picked from the given type */ public Pick(type: Type, key: readonly [...Key], options?: SchemaOptions): TPick @@ -264,12 +264,12 @@ export class JsonTypeBuilder { return Ref($ref, options) } /** `[Json]` Constructs a type where all properties are required */ - public Required(T: T, options?: SchemaOptions): TRequiredFromMappedResult + public Required(mappedResult: MappedResult, options?: SchemaOptions): TRequiredFromMappedResult /** `[Json]` Constructs a type where all properties are required */ - public Required(schema: T, options?: SchemaOptions): TRequired + public Required(type: Type, options?: SchemaOptions): TRequired /** `[Json]` Constructs a type where all properties are required */ - public Required(schema: TSchema, options?: SchemaOptions): any { - return Required(schema, options) + public Required(type: TSchema, options?: SchemaOptions): any { + return Required(type, options) } /** `[Json]` Extracts interior Rest elements from Tuple, Intersect and Union types */ public Rest(schema: T): TRest {