diff --git a/src/typebox.ts b/src/typebox.ts index 64bb30df1..c6d45c402 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -242,9 +242,10 @@ export interface TArray extends TSchema, ArrayOptio // -------------------------------------------------------------------------- // TAsyncIterator // -------------------------------------------------------------------------- +export type TAsyncIteratorResolve = Ensure>> export interface TAsyncIterator extends TSchema { [Kind]: 'AsyncIterator' - static: AsyncIterableIterator> + static: TAsyncIteratorResolve type: 'AsyncIterator' items: T } @@ -280,7 +281,7 @@ export interface TBoolean extends TSchema { // -------------------------------------------------------------------------- // TConstructorParameters // -------------------------------------------------------------------------- -export type TConstructorParameters> = TTuple +export type TConstructorParameters> = Ensure> // -------------------------------------------------------------------------- // TInstanceType // -------------------------------------------------------------------------- @@ -307,10 +308,11 @@ export type TComposite = TIntersect extends TIntersect // -------------------------------------------------------------------------- // TConstructor // -------------------------------------------------------------------------- -export type TConstructorParameterArray = [...{ [K in keyof T]: Static, P> }] +export type TConstructorReturnType = Static +export type TConstructorParameterArray = T extends [infer L extends TSchema, ...infer R extends TSchema[]] ? [Static, ...TFunctionParameters] : [] export interface TConstructor extends TSchema { [Kind]: 'Constructor' - static: new (...param: TConstructorParameterArray) => Static + static: new (...param: TConstructorParameterArray) => TConstructorReturnType type: 'Constructor' parameters: T returns: U @@ -386,10 +388,11 @@ export type TExtract = // -------------------------------------------------------------------------- // TFunction // -------------------------------------------------------------------------- -export type TFunctionParameters = [...{ [K in keyof T]: Static, P> }] +export type TFunctionReturnType = Static +export type TFunctionParameters = T extends [infer L extends TSchema, ...infer R extends TSchema[]] ? [Static, ...TFunctionParameters] : [] export interface TFunction extends TSchema { [Kind]: 'Function' - static: (...param: TFunctionParameters) => Static + static: (...param: TFunctionParameters) => TFunctionReturnType type: 'Function' parameters: T returns: U @@ -472,9 +475,10 @@ export interface TIntersect extends TSchema, In // -------------------------------------------------------------------------- // TIterator // -------------------------------------------------------------------------- +export type TIteratorResolve = Ensure>> export interface TIterator extends TSchema { [Kind]: 'Iterator' - static: IterableIterator> + static: TIteratorResolve type: 'Iterator' items: T } @@ -880,9 +884,9 @@ export type DecodeType = ( T extends TTransform ? TUnsafe : T extends TArray ? TArray> : T extends TAsyncIterator ? TAsyncIterator> : - T extends TConstructor ? TConstructor> : + T extends TConstructor ? TConstructor, DecodeType> : T extends TEnum ? TEnum : // intercept for union. interior non decodable - T extends TFunction ? TFunction> : + T extends TFunction ? TFunction, DecodeType> : T extends TIntersect ? TIntersect> : T extends TIterator ? TIterator> : T extends TNot ? TNot> : @@ -3313,7 +3317,7 @@ export class JavaScriptTypeBuilder extends JsonTypeBuilder { return this.Create({ ...options, [Kind]: 'BigInt', type: 'bigint' }) } /** `[JavaScript]` Extracts the ConstructorParameters from the given Constructor type */ - public ConstructorParameters>(schema: T, options: SchemaOptions = {}): TConstructorParameters { + public ConstructorParameters>(schema: T, options: SchemaOptions = {}): TConstructorParameters { return this.Tuple([...schema.parameters], { ...options }) } /** `[JavaScript]` Creates a Constructor type */ @@ -3339,7 +3343,7 @@ export class JavaScriptTypeBuilder extends JsonTypeBuilder { return this.Create({ ...options, [Kind]: 'Iterator', type: 'Iterator', items: TypeClone.Type(items) }) } /** `[JavaScript]` Extracts the Parameters from the given Function type */ - public Parameters>(schema: T, options: SchemaOptions = {}): TParameters { + public Parameters>(schema: T, options: SchemaOptions = {}): TParameters { return this.Tuple(schema.parameters, { ...options }) } /** `[JavaScript]` Creates a Promise type */ diff --git a/test/static/constructor.ts b/test/static/constructor.ts index a05363991..c91d398e7 100644 --- a/test/static/constructor.ts +++ b/test/static/constructor.ts @@ -1,11 +1,31 @@ import { Expect } from './assert' import { Type } from '@sinclair/typebox' - -const C = Type.Constructor( - [Type.Number(), Type.String()], - Type.Object({ - method: Type.Function([Type.Number(), Type.String()], Type.Boolean()), - }), -) - -Expect(C).ToStatic { method: (param_0: number, param_1: string) => boolean }>() +{ + // simple + const T = Type.Constructor([Type.Number(), Type.Boolean()], Type.String()) + Expect(T).ToStatic string>() +} +{ + // nested + // prettier-ignore + const T = Type.Constructor([Type.Number(), Type.String()], Type.Object({ + method: Type.Constructor([Type.Number(), Type.String()], Type.Boolean()), + })) + Expect(T).ToStatic { method: new (param_0: number, param_1: string) => boolean }>() +} +{ + // decode 2 + const S = Type.Transform(Type.Integer()) + .Decode((value) => new Date(value)) + .Encode((value) => value.getTime()) + const T = Type.Constructor([S], Type.String()) + Expect(T).ToStaticDecode string>() +} +{ + // decode 1 + const S = Type.Transform(Type.Integer()) + .Decode((value) => new Date(value)) + .Encode((value) => value.getTime()) + const T = Type.Constructor([Type.Number()], S) + Expect(T).ToStaticDecode Date>() +} diff --git a/test/static/function.ts b/test/static/function.ts index 623aa911e..a204187a6 100644 --- a/test/static/function.ts +++ b/test/static/function.ts @@ -1,11 +1,32 @@ import { Expect } from './assert' import { Type } from '@sinclair/typebox' -const C = Type.Function( - [Type.Number(), Type.String()], - Type.Object({ +{ + // simple + const T = Type.Function([Type.Number(), Type.Boolean()], Type.String()) + Expect(T).ToStatic<(param_0: number, param_1: boolean) => string>() +} +{ + // nested + // prettier-ignore + const T = Type.Function([Type.Number(), Type.String()], Type.Object({ method: Type.Function([Type.Number(), Type.String()], Type.Boolean()), - }), -) - -Expect(C).ToStatic<(param_0: number, param_1: string) => { method: (param_0: number, param_1: string) => boolean }>() + })) + Expect(T).ToStatic<(param_0: number, param_1: string) => { method: (param_0: number, param_1: string) => boolean }>() +} +{ + // decode 2 + const S = Type.Transform(Type.Integer()) + .Decode((value) => new Date(value)) + .Encode((value) => value.getTime()) + const T = Type.Function([S], Type.String()) + Expect(T).ToStaticDecode<(param_0: Date) => string>() +} +{ + // decode 1 + const S = Type.Transform(Type.Integer()) + .Decode((value) => new Date(value)) + .Encode((value) => value.getTime()) + const T = Type.Function([Type.Number()], S) + Expect(T).ToStaticDecode<(param_0: number) => Date>() +} diff --git a/test/static/transform.ts b/test/static/transform.ts index 468787ef2..917628a67 100644 --- a/test/static/transform.ts +++ b/test/static/transform.ts @@ -291,3 +291,21 @@ import { Expect } from './assert' Expect(T).ToStaticDecode<1>() Expect(T).ToStaticEncode() } +{ + // should transform functions + const S = Type.Transform(Type.Number()) + .Decode((value) => new Date(value)) + .Encode((value) => value.getTime()) + const T = Type.Function([S], S) + Expect(T).ToStaticDecode<(x: Date) => Date>() + Expect(T).ToStaticEncode<(x: number) => number>() +} +{ + // should transform constructors + const S = Type.Transform(Type.Number()) + .Decode((value) => new Date(value)) + .Encode((value) => value.getTime()) + const T = Type.Constructor([S], S) + Expect(T).ToStaticDecode Date>() + Expect(T).ToStaticEncode number>() +}