From 67925c10a36c786e538d502034c7af08397e52e9 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Tue, 29 Aug 2023 18:24:26 +0900 Subject: [PATCH] Revision 0.31.6 (#560) --- package-lock.json | 4 +-- package.json | 2 +- src/typebox.ts | 47 +++++++++++++++----------------- src/value/value.ts | 8 +++--- test/static/readonly-optional.ts | 19 ++++++++++++- test/static/transform.ts | 33 +++++++++++++++++++++- 6 files changed, 79 insertions(+), 34 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3d79d5069..c9126a5d3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.31.5", + "version": "0.31.6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.31.5", + "version": "0.31.6", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index 7d45a33f4..d4ca52eea 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.31.5", + "version": "0.31.6", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/typebox.ts b/src/typebox.ts index d4f7ca769..b89bab520 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -66,7 +66,7 @@ export type AssertType = T extends E ? T : TNeve // -------------------------------------------------------------------------- // Modifiers // -------------------------------------------------------------------------- -export type TModifier = TOptional | TReadonly +export type TReadonlyOptional = TOptional & TReadonly export type TReadonly = T & { [Readonly]: 'Readonly' } export type TOptional = T & { [Optional]: 'Optional' } // -------------------------------------------------------------------------- @@ -855,37 +855,34 @@ export interface TTemplateLiteral = - T extends TReadonly & TOptional ? TReadonly> : - T extends TReadonly ? TReadonly : - T extends TOptional ? TOptional : - D -// prettier-ignore export type DecodeProperties = { [K in keyof T]: DecodeType } // prettier-ignore -export type DecodeRest = T extends [infer L, ...infer R] - ? [DecodeType>, ...DecodeRest>] +export type DecodeRest = T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? [DecodeType, ...DecodeRest] : [] // prettier-ignore -export type DecodeType = - T extends TTransform ? DecodeModifier, T> : - T extends TArray ? DecodeModifier>, T> : - T extends TAsyncIterator ? DecodeModifier>, T> : - T extends TConstructor ? DecodeModifier>, DecodeType>, T> : - T extends TFunction ? DecodeModifier>, DecodeType>, T> : - T extends TIntersect ? DecodeModifier>>, T> : - T extends TIterator ? DecodeModifier>, T> : - T extends TNot ? DecodeModifier>, T> : - T extends TObject ? DecodeModifier>>, T> : - T extends TPromise ? DecodeModifier>, T> : - T extends TRecord ? DecodeModifier>, T> : - T extends TRecursive ? DecodeModifier>, T> : - T extends TRef ? DecodeModifier>, T> : - T extends TTuple ? DecodeModifier>>, T> : - T extends TUnion ? DecodeModifier>>, T> : +export type DecodeType = ( + T extends TOptional ? TOptional> : + T extends TReadonly ? TReadonly> : + T extends TTransform ? TUnsafe : + T extends TArray ? Array> : + T extends TAsyncIterator ? TAsyncIterator> : + T extends TConstructor ? TConstructor> : + T extends TFunction ? TFunction> : + T extends TIntersect ? TIntersect : + T extends TIterator ? TIterator> : + T extends TNot ? TNot> : + T extends TObject ? TObject>> : + T extends TPromise ? TPromise> : + T extends TRecord ? TRecord> : + T extends TRecursive ? TRecursive> : + T extends TRef ? TRef> : + T extends TTuple ? TTuple : + T extends TUnion ? TUnion : T +) export type TransformFunction = (value: T) => U export interface TransformOptions { Decode: TransformFunction, O> diff --git a/src/value/value.ts b/src/value/value.ts index 59b8dbe00..edeec7dae 100644 --- a/src/value/value.ts +++ b/src/value/value.ts @@ -78,9 +78,9 @@ export namespace Value { return ValueClone.Clone(value) } /** Decodes a value or throws if error */ - export function Decode(schema: T, references: Types.TSchema[], value: unknown): Types.StaticDecode + export function Decode>(schema: T, references: Types.TSchema[], value: unknown): D /** Decodes a value or throws if error */ - export function Decode(schema: T, value: unknown): Types.StaticDecode + export function Decode>(schema: T, value: unknown): D /** Decodes a value or throws if error */ export function Decode(...args: any[]) { const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] @@ -88,9 +88,9 @@ export namespace Value { return ValueTransform.DecodeTransform.Decode(schema, references, value, ValueCheck.Check) } /** Encodes a value or throws if error */ - export function Encode(schema: T, references: Types.TSchema[], value: unknown): Types.StaticEncode + export function Encode>(schema: T, references: Types.TSchema[], value: unknown): E /** Encodes a value or throws if error */ - export function Encode(schema: T, value: unknown): Types.StaticEncode + export function Encode>(schema: T, value: unknown): E /** Encodes a value or throws if error */ export function Encode(...args: any[]) { const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] diff --git a/test/static/readonly-optional.ts b/test/static/readonly-optional.ts index 3191dcd75..aca58d0ba 100644 --- a/test/static/readonly-optional.ts +++ b/test/static/readonly-optional.ts @@ -1,5 +1,5 @@ import { Expect } from './assert' -import { Type, TSchema } from '@sinclair/typebox' +import { Type, TSchema, TReadonlyOptional } from '@sinclair/typebox' { const T = Type.Object({ @@ -9,3 +9,20 @@ import { Type, TSchema } from '@sinclair/typebox' readonly A?: string }>() } +{ + const T = Type.ReadonlyOptional(Type.String()) + function test(_: TReadonlyOptional) {} + test(T) +} +{ + const T = Type.Readonly(Type.String()) + function test(_: TReadonlyOptional) {} + // @ts-expect-error + test(T) +} +{ + const T = Type.Optional(Type.String()) + function test(_: TReadonlyOptional) {} + // @ts-expect-error + test(T) +} \ No newline at end of file diff --git a/test/static/transform.ts b/test/static/transform.ts index 3e02df125..e6770917e 100644 --- a/test/static/transform.ts +++ b/test/static/transform.ts @@ -1,4 +1,4 @@ -import { Type, Static, StaticDecode, TObject, TNumber } from '@sinclair/typebox' +import { Type, TSchema, Static, StaticDecode, TObject, TNumber } from '@sinclair/typebox' import { Expect } from './assert' { // string > number @@ -214,3 +214,34 @@ import { Expect } from './assert' Expect(T).ToStaticDecode<{ x: '1' }>() Expect(T).ToStaticDecode<{ x: undefined }>() } +{ // should decode within generic function context + // https://github.com/sinclairzx81/typebox/issues/554 + // prettier-ignore + const ArrayOrSingle = (schema: T) => + Type.Transform(Type.Union([schema, Type.Array(schema)])) + .Decode((value) => (Array.isArray(value) ? value : [value])) + .Encode((value) => (value.length === 1 ? value[0] : value) as Static[]); + const T = ArrayOrSingle(Type.String()) + Expect(T).ToStaticDecode() +} +{ + // should correctly decode record keys + // https://github.com/sinclairzx81/typebox/issues/555 + // prettier-ignore + const T = Type.Object({ + x: Type.Optional(Type.Record(Type.Number(), Type.String())) + }) + type A = StaticDecode + type Test = E extends A ? true : false + type E1 = Test + type E2 = Test + type E3 = Test + type E4 = Test + type E5 = Test + // assignment + const E1: E1 = true + const E2: E2 = true + const E3: E3 = true + const E4: E4 = false + const E5: E5 = true +}