From 9cb7b028a9267bb0ee96eda23760dca7937892af Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 15 Nov 2023 15:26:34 +0900 Subject: [PATCH] Revision 0.31.24 (#664) * Improve Function and Constructor Inference * Comment updates on Static types --- package-lock.json | 4 ++-- package.json | 2 +- src/typebox.ts | 34 ++++++++++++++++++++-------------- test/static/constructor.ts | 38 +++++++++++++++++++++++++++++--------- test/static/function.ts | 35 ++++++++++++++++++++++++++++------- test/static/transform.ts | 18 ++++++++++++++++++ 6 files changed, 98 insertions(+), 33 deletions(-) diff --git a/package-lock.json b/package-lock.json index 92ca683d4..184112058 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.31.23", + "version": "0.31.24", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.31.23", + "version": "0.31.24", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.18.0", diff --git a/package.json b/package.json index cc5ea9f99..471eb0a1a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.31.23", + "version": "0.31.24", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/typebox.ts b/src/typebox.ts index 64bb30df1..9afde820d 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,12 @@ export type TComposite = TIntersect extends TIntersect // -------------------------------------------------------------------------- // TConstructor // -------------------------------------------------------------------------- -export type TConstructorParameterArray = [...{ [K in keyof T]: Static, P> }] +export type TConstructorReturnTypeResolve = Static +export type TConstructorParametersResolve = T extends [infer L extends TSchema, ...infer R extends TSchema[]] ? [Static, ...TFunctionParametersResolve] : [] +export type TConstructorResolve = Ensure) => TConstructorReturnTypeResolve> export interface TConstructor extends TSchema { [Kind]: 'Constructor' - static: new (...param: TConstructorParameterArray) => Static + static: TConstructorResolve type: 'Constructor' parameters: T returns: U @@ -386,10 +389,12 @@ export type TExtract = // -------------------------------------------------------------------------- // TFunction // -------------------------------------------------------------------------- -export type TFunctionParameters = [...{ [K in keyof T]: Static, P> }] +export type TFunctionReturnTypeResolve = Static +export type TFunctionParametersResolve = T extends [infer L extends TSchema, ...infer R extends TSchema[]] ? [Static, ...TFunctionParametersResolve] : [] +export type TFunctionResolve = Ensure<(...param: TFunctionParametersResolve) => TFunctionReturnTypeResolve> export interface TFunction extends TSchema { [Kind]: 'Function' - static: (...param: TFunctionParameters) => Static + static: TFunctionResolve type: 'Function' parameters: T returns: U @@ -472,9 +477,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 +886,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> : @@ -988,11 +994,11 @@ export interface TVoid extends TSchema { // -------------------------------------------------------------------------- // Static // -------------------------------------------------------------------------- -/** Creates the decoded static form for a TypeBox type */ +/** Creates an decoded static type from a TypeBox type */ export type StaticDecode = Static, P> -/** Creates the encoded static form for a TypeBox type */ +/** Creates an encoded static type from a TypeBox type */ export type StaticEncode = Static -/** Creates the static type for a TypeBox type */ +/** Creates a static type from a TypeBox type */ export type Static = (T & { params: P })['static'] // -------------------------------------------------------------------------- // TypeRegistry @@ -3313,7 +3319,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 +3345,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>() +}