This commit is contained in:
sinclair
2025-12-24 15:44:34 +09:00
commit 13d553220c
1047 changed files with 80931 additions and 0 deletions

4
test/static/any.ts Normal file
View File

@@ -0,0 +1,4 @@
import { Expect } from './assert'
import { Type } from '@sinclair/typebox'
Expect(Type.Any()).ToStatic<any>()

21
test/static/argument.ts Normal file
View File

@@ -0,0 +1,21 @@
import { Expect } from './assert'
import { Type } from '@sinclair/typebox'
const T = Type.Object({
x: Type.Argument(0),
y: Type.Argument(1),
z: Type.Argument(2),
})
const I = Type.Instantiate(T, [Type.Literal(1), Type.Literal(2), Type.Literal(3)])
// Infer as Broadest Type (Pending Generic Constraints)
Expect(T).ToStatic<{
x: unknown
y: unknown
z: unknown
}>()
// Infer as Narrowed Type
Expect(I).ToStatic<{
x: 1
y: 2
z: 3
}>()

24
test/static/array.ts Normal file
View File

@@ -0,0 +1,24 @@
import { Expect } from './assert'
import { Type } from '@sinclair/typebox'
Expect(Type.Array(Type.String())).ToStatic<string[]>()
Expect(
Type.Array(
Type.Object({
x: Type.Number(),
y: Type.Boolean(),
z: Type.String(),
}),
),
).ToStatic<
{
x: number
y: boolean
z: string
}[]
>()
Expect(Type.Array(Type.Array(Type.String()))).ToStatic<string[][]>()
Expect(Type.Array(Type.Tuple([Type.String(), Type.Number()]))).ToStatic<[string, number][]>()

70
test/static/assert.ts Normal file
View File

@@ -0,0 +1,70 @@
import { Static, StaticDecode, StaticEncode, TSchema } from '@sinclair/typebox'
// ------------------------------------------------------------------
// Symbols
// ------------------------------------------------------------------
export declare const Unsatisfiable: unique symbol
// Warning: `never` and `any` satisfy the constraint `extends Expected<...>`
export type Expected<_> = { [Unsatisfiable]: never }
// ------------------------------------------------------------------
// Gates
// ------------------------------------------------------------------
export type If<T, Y, N> = T extends true ? Y : N
export type And<T, U> = If<T, U, false>
export type Or<T, U> = If<T, true, U>
export type Not<T> = If<T, false, true>
// ------------------------------------------------------------------
// Helpers
// ------------------------------------------------------------------
export type Extends<T, U> = [T] extends [U] ? true : false
export type IsAny<T> = 0 extends 1 & T ? true : false
export type IsNever<T> = Extends<T, never>
// ------------------------------------------------------------------
// Constraints
// ------------------------------------------------------------------
// See https://github.com/microsoft/TypeScript/issues/51011
export type CircularHelper<T, U> = [T] extends U ? T : Expected<T>
// See https://github.com/Microsoft/TypeScript/issues/27024
export type ConstrainEqual<T, U> = (<V>() => V extends T ? 1 : 2) extends <V>() => V extends U ? 1 : 2 ? T : Expected<T>
export type ConstraintMutuallyExtend<T, U> = CircularHelper<T, [U]>
// Circular Error on TS 5.4.0
// If U is never, there's nothing we can do
// export type ComplexConstraint<T, U> = If<
// // If U is any, we can't use Expect<T> or it would satisfy the constraint
// And<Not<IsAny<T>>, IsAny<U>>,
// never,
// If<
// Or<
// // If they are both any we are happy
// And<IsAny<T>, IsAny<U>>,
// // If T extends U, but not because it's any, we are happy
// And<Extends<T, U>, Not<IsAny<T>>>
// >,
// T,
// Expected<T>
// >
// >
// ------------------------------------------------------------------
// Expect
// ------------------------------------------------------------------
export type ExpectResult<T extends TSchema> = If<
IsNever<Static<T>>,
{ ToStaticNever(): void },
{
ToStatic<U extends ConstraintMutuallyExtend<Static<T>, U>>(): void
ToStaticDecode<U extends ConstraintMutuallyExtend<StaticDecode<T>, U>>(): void
ToStaticEncode<U extends ConstraintMutuallyExtend<StaticEncode<T>, U>>(): void
// ToStatic<U extends Static<T>>(): void
// ToStaticDecode<U extends StaticDecode<T>>(): void
// ToStaticEncode<U extends StaticEncode<T>>(): void
}
>
export function Expect<T extends TSchema>(schema: T) {
return {
ToStatic() {},
ToStaticNever() {},
ToStaticDecode() {},
ToStaticEncode() {},
} as ExpectResult<T>
}

View File

@@ -0,0 +1,4 @@
import { Expect } from './assert'
import { Type } from '@sinclair/typebox'
Expect(Type.AsyncIterator(Type.String())).ToStatic<AsyncIterableIterator<string>>()

20
test/static/awaited.ts Normal file
View File

@@ -0,0 +1,20 @@
import { Expect } from './assert'
import { Type } from '@sinclair/typebox'
Expect(Type.Awaited(Type.String())).ToStatic<string>()
Expect(Type.Awaited(Type.Promise(Type.String()))).ToStatic<string>()
Expect(Type.Awaited(Type.Promise(Type.Promise(Type.String())))).ToStatic<string>()
// One Level
Expect(Type.Awaited(Type.Union([Type.Promise(Type.String()), Type.Number()]))).ToStatic<string | number>()
Expect(Type.Awaited(Type.Intersect([Type.Promise(Type.Object({ a: Type.String() })), Type.Object({ b: Type.Number() })]))).ToStatic<{ a: string } & { b: number }>()
// Two Levels
Expect(Type.Awaited(Type.Union([Type.Promise(Type.Promise(Type.String())), Type.Number()]))).ToStatic<string | number>()
Expect(Type.Awaited(Type.Intersect([Type.Promise(Type.Promise(Type.Object({ a: Type.String() }))), Type.Object({ b: Type.Number() })]))).ToStatic<{ a: string } & { b: number }>()

4
test/static/bigint.ts Normal file
View File

@@ -0,0 +1,4 @@
import { Expect } from './assert'
import { Type } from '@sinclair/typebox'
Expect(Type.BigInt()).ToStatic<bigint>()

4
test/static/boolean.ts Normal file
View File

@@ -0,0 +1,4 @@
import { Expect } from './assert'
import { Type } from '@sinclair/typebox'
Expect(Type.Boolean()).ToStatic<boolean>()

14
test/static/capitalize.ts Normal file
View File

@@ -0,0 +1,14 @@
import { Expect } from './assert'
import { Type } from '@sinclair/typebox'
Expect(Type.Capitalize(Type.Literal('hello'))).ToStatic<'Hello'>()
Expect(Type.Capitalize(Type.Union([Type.Literal('hello'), Type.Literal('world')]))).ToStatic<'Hello' | 'World'>()
Expect(Type.Capitalize(Type.TemplateLiteral('hello${0|1}'))).ToStatic<'Hello0' | 'Hello1'>()
// prettier-ignore
Expect(Type.Capitalize(Type.TemplateLiteral([Type.Literal('hello'), Type.Union([Type.Literal(1), Type.Literal(2)])]))).ToStatic<'Hello1' | 'Hello2'>()
// passthrough
Expect(Type.Capitalize(Type.Object({ x: Type.Number() }))).ToStatic<{ x: number }>()

213
test/static/composite.ts Normal file
View File

@@ -0,0 +1,213 @@
import { Expect } from './assert'
import { Type, TOptional, TObject, TUnion, TIntersect, TNumber, TString, TBoolean } from '@sinclair/typebox'
// ----------------------------------------------------------------------------
// Overlapping - Non Varying
// ----------------------------------------------------------------------------
{
const A = Type.Object({
A: Type.Number(),
})
const B = Type.Object({
A: Type.Number(),
})
const T = Type.Composite([A, B])
Expect(T).ToStatic<{
A: number
}>()
}
// ----------------------------------------------------------------------------
// Overlapping - Varying
// ----------------------------------------------------------------------------
{
const A = Type.Object({
A: Type.Number(),
})
const B = Type.Object({
A: Type.String(),
})
const T = Type.Composite([A, B])
Expect(T).ToStatic<{
A: never
}>()
}
// ----------------------------------------------------------------------------
// Overlapping Single Optional
// ----------------------------------------------------------------------------
{
const A = Type.Object({
A: Type.Optional(Type.Number()),
})
const B = Type.Object({
A: Type.Number(),
})
const T = Type.Composite([A, B])
Expect(T).ToStatic<{
A: number
}>()
}
// ----------------------------------------------------------------------------
// Overlapping All Optional (Deferred)
//
// Note for: https://github.com/sinclairzx81/typebox/issues/419
// ----------------------------------------------------------------------------
{
const A = Type.Object({
A: Type.Optional(Type.Number()),
})
const B = Type.Object({
A: Type.Optional(Type.Number()),
})
const T = Type.Composite([A, B])
Expect(T).ToStatic<{
A?: number | undefined
}>()
}
{
const A = Type.Object({
A: Type.Optional(Type.Number()),
})
const B = Type.Object({
A: Type.Number(),
})
const T = Type.Composite([A, B])
Expect(T).ToStatic<{
A: number
}>()
}
{
const A = Type.Object({
A: Type.Number(),
})
const B = Type.Object({
A: Type.Number(),
})
const T = Type.Composite([A, B])
Expect(T).ToStatic<{
A: number
}>()
}
// ----------------------------------------------------------------------------
// Distinct Properties
// ----------------------------------------------------------------------------
{
const A = Type.Object({
A: Type.Number(),
})
const B = Type.Object({
B: Type.Number(),
})
const T = Type.Composite([A, B])
Expect(T).ToStatic<{
A: number
B: number
}>()
}
// ----------------------------------------------------------------------------
// Intersection Quirk
//
// TypeScript has an evaluation quirk for the following case where the first
// type evaluates the sub property as never, but the second evaluates the
// entire type as never. There is probably a reason for this behavior, but
// TypeBox supports the former evaluation.
//
// { x: number } & { x: string } -> { x: number } & { x: string } => { x: never }
// { x: number } & { x: boolean } -> never -> ...
// ----------------------------------------------------------------------------
{
// prettier-ignore
const T: TObject<{
x: TIntersect<[TNumber, TBoolean]>
}> = Type.Composite([
Type.Object({ x: Type.Number() }),
Type.Object({ x: Type.Boolean() })
])
}
// ------------------------------------------------------------------
// Intersect
// ------------------------------------------------------------------
// prettier-ignore
{
const T: TObject<{
x: TNumber;
y: TNumber;
z: TNumber;
}> = Type.Composite([
Type.Intersect([
Type.Object({ x: Type.Number() }),
Type.Object({ y: Type.Number() }),
]),
Type.Intersect([
Type.Object({ z: Type.Number() })
])
])
}
// prettier-ignore
{
const T: TObject<{
x: TIntersect<[TNumber, TNumber]>;
y: TIntersect<[TNumber, TNumber]>;
}> = Type.Composite([
Type.Intersect([
Type.Object({ x: Type.Number() }),
Type.Object({ y: Type.Number() }),
]),
Type.Intersect([
Type.Object({ x: Type.Number() }),
Type.Object({ y: Type.Number() }),
])
])
}
// prettier-ignore
{
const T: TObject<{
x: TIntersect<[TNumber, TNumber]>;
}> = Type.Composite([
Type.Intersect([
Type.Object({ x: Type.Optional(Type.Number()) }),
Type.Object({ x: Type.Number() }),
])
])
}
// prettier-ignore
{
const T: TObject<{
x: TOptional<TIntersect<[TNumber, TNumber]>>;
}> = Type.Composite([
Type.Intersect([
Type.Object({ x: Type.Optional(Type.Number()) }),
Type.Object({ x: Type.Optional(Type.Number()) }),
])
])
}
// ------------------------------------------------------------------
// Union
// ------------------------------------------------------------------
// prettier-ignore
{
const T: TObject<{
x: TNumber;
}> = Type.Composite([
Type.Union([
Type.Object({ x: Type.Number() }),
Type.Object({ y: Type.Number() })
]),
Type.Object({ x: Type.Number() })
])
}
// prettier-ignore
{
const T: TObject<{
x: TIntersect<[TUnion<[TString, TString]>, TNumber]>;
}> = Type.Composite([
Type.Union([
Type.Object({ x: Type.String() }),
Type.Object({ x: Type.String() })
]),
Type.Object({ x: Type.Number() })
])
}

41
test/static/const.ts Normal file
View File

@@ -0,0 +1,41 @@
import { Expect } from './assert'
import { Type } from '@sinclair/typebox'
// ------------------------------------------------------------------
// Identity Types
// ------------------------------------------------------------------
// prettier-ignore
Expect(Type.Const(undefined)).ToStatic<undefined>()
// prettier-ignore
Expect(Type.Const(null)).ToStatic<null>()
// prettier-ignore
Expect(Type.Const(Symbol())).ToStatic<symbol>()
// prettier-ignore
Expect(Type.Const(1 as const)).ToStatic<1>()
// prettier-ignore
Expect(Type.Const('hello' as const)).ToStatic<'hello'>()
// prettier-ignore
Expect(Type.Const(true as const)).ToStatic<true>()
// ------------------------------------------------------------------
// Complex Types
// ------------------------------------------------------------------
// prettier-ignore
Expect(Type.Const(100n)).ToStatic<bigint>()
// prettier-ignore
Expect(Type.Const(new Date())).ToStatic<Date>()
// prettier-ignore
Expect(Type.Const(new Uint8Array())).ToStatic<Uint8Array>()
// prettier-ignore
Expect(Type.Const(function () {})).ToStatic<() => unknown>()
// prettier-ignore
Expect(Type.Const((function *(): any {})())).ToStatic<any>()
// prettier-ignore
Expect(Type.Const((async function *(): any {})())).ToStatic<any>()
// todo: remove when dropping TS 4.0
// prettier-ignore
Expect(Type.Const({ x: 1, y: { z: 2 } })).ToStatic<{ readonly x: number, readonly y: { readonly z: number }}>()
// prettier-ignore
Expect(Type.Const({ x: 1, y: { z: 2 } } as const)).ToStatic<{ readonly x: 1, readonly y: { readonly z: 2 }}>()
// prettier-ignore
Expect(Type.Const([1, 2, 3] as const)).ToStatic<[1, 2, 3]>()

View File

@@ -0,0 +1,13 @@
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()),
}),
)
const P = Type.ConstructorParameters(C)
Expect(P).ToStatic<[number, string]>()

View File

@@ -0,0 +1,51 @@
import { Expect } from './assert'
import { Type } from '@sinclair/typebox'
{
// simple
const T = Type.Constructor([Type.Number(), Type.Boolean()], Type.String())
Expect(T).ToStatic<new (param_0: number, param_1: boolean) => 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<new (param_0: number, param_1: string) => { method: new (param_0: number, param_1: string) => boolean }>()
}
{
// readonly-optional
const T = Type.Constructor([Type.ReadonlyOptional(Type.Array(Type.Number()))], Type.Number())
Expect(T).ToStaticDecode<new (param_0?: readonly number[]) => number>()
}
{
// readonly
const T = Type.Constructor([Type.Readonly(Type.Array(Type.Number()))], Type.Number())
Expect(T).ToStaticDecode<new (param_0: readonly number[]) => number>()
}
{
// optional 1
const T = Type.Constructor([Type.Optional(Type.Number())], Type.Number())
Expect(T).ToStaticDecode<new (param_0?: number) => number>()
}
{
// optional 2
const T = Type.Constructor([Type.Number(), Type.Optional(Type.Number())], Type.Number())
Expect(T).ToStaticDecode<new (param_0: number, param_1?: number) => number>()
}
{
// 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<new (param_0: Date) => 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<new (param_0: number) => Date>()
}

4
test/static/date.ts Normal file
View File

@@ -0,0 +1,4 @@
import { Expect } from './assert'
import { Type } from '@sinclair/typebox'
Expect(Type.Date()).ToStatic<Date>()

42
test/static/enum.ts Normal file
View File

@@ -0,0 +1,42 @@
import { Expect } from './assert'
import { Type } from '@sinclair/typebox'
{
// expect all variants
enum E {
A,
B = 'hello',
C = 42,
}
const T = Type.Enum(E)
Expect(T).ToStatic<E.A | E.B | E.C>()
}
{
// expect all variants
const T = Type.Enum({
A: 1,
B: 2,
C: 3,
})
Expect(T).ToStatic<1 | 2 | 3>()
}
{
// expect variant overlap to reduce
const T = Type.Enum({
A: 1,
B: 2,
C: 2, // overlap
})
Expect(T).ToStatic<1 | 2>()
}
{
// expect empty enum to be string (as empty enums T[keyof T] evaluates as string)
enum E {}
const T = Type.Enum(E)
Expect(T).ToStatic<string>()
}
{
// expect empty enum to be never
const T = Type.Enum({})
Expect(T).ToStaticNever()
}

99
test/static/exclude.ts Normal file
View File

@@ -0,0 +1,99 @@
import { Type, TLiteral, TUnion } from '@sinclair/typebox'
import { Expect } from './assert'
{
const T = Type.Exclude(Type.String(), Type.String())
Expect(T).ToStaticNever()
}
{
const T = Type.Exclude(Type.String(), Type.Number())
Expect(T).ToStatic<string>()
}
{
const T = Type.Exclude(Type.Union([Type.Number(), Type.String(), Type.Boolean()]), Type.Number())
Expect(T).ToStatic<boolean | string>()
}
// ------------------------------------------------------------------------
// TemplateLiteral | TemplateLiteral
// ------------------------------------------------------------------------
{
const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])])
const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])])
const T = Type.Exclude(A, B)
Expect(T).ToStaticNever()
}
{
const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])])
const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B')])])
const T = Type.Exclude(A, B)
Expect(T).ToStatic<'C'>()
}
{
const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])])
const B = Type.TemplateLiteral([Type.Union([Type.Literal('A')])])
const T = Type.Exclude(A, B)
Expect(T).ToStatic<'C' | 'B'>()
}
// ------------------------------------------------------------------------
// TemplateLiteral | Union
// ------------------------------------------------------------------------
{
const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])])
const B = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])
const T = Type.Exclude(A, B)
Expect(T).ToStaticNever()
}
{
const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])])
const B = Type.Union([Type.Literal('A'), Type.Literal('B')])
const T = Type.Exclude(A, B)
Expect(T).ToStatic<'C'>()
}
{
const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])])
const B = Type.Union([Type.Literal('A')])
const T = Type.Exclude(A, B)
Expect(T).ToStatic<'C' | 'B'>()
}
// ------------------------------------------------------------------------
// Union | TemplateLiteral
// ------------------------------------------------------------------------
{
const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])
const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])])
const T = Type.Exclude(A, B)
Expect(T).ToStaticNever()
}
{
const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])
const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B')])])
const T = Type.Exclude(A, B)
Expect(T).ToStatic<'C'>()
}
{
const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])
const B = Type.TemplateLiteral([Type.Union([Type.Literal('A')])])
const T = Type.Exclude(A, B)
Expect(T).ToStatic<'C' | 'B'>()
}
// https://github.com/sinclairzx81/typebox/issues/737
{
const U = Type.Union([Type.Literal('A'), Type.Literal('B')])
const T = Type.Object({
type: Type.Exclude(U, Type.Literal('A')),
})
Expect(T).ToStatic<{ type: 'B' }>()
}
{
const U = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])
const T = Type.Object({
type: Type.Exclude(U, Type.Literal('A')),
})
Expect(T).ToStatic<{ type: 'B' | 'C' }>()
}

100
test/static/extract.ts Normal file
View File

@@ -0,0 +1,100 @@
import { Type, TLiteral, TUnion } from '@sinclair/typebox'
import { Expect } from './assert'
{
const T = Type.Extract(Type.String(), Type.String())
Expect(T).ToStatic<string>()
}
{
const T = Type.Extract(Type.String(), Type.Number())
Expect(T).ToStaticNever()
}
{
const T = Type.Extract(Type.Union([Type.Number(), Type.String(), Type.Boolean()]), Type.Number())
Expect(T).ToStatic<number>()
}
// ------------------------------------------------------------------------
// TemplateLiteral | TemplateLiteral
// ------------------------------------------------------------------------
{
const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])])
const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])])
const T = Type.Extract(A, B)
Expect(T).ToStatic<'A' | 'B' | 'C'>()
}
{
const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])])
const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B')])])
const T = Type.Extract(A, B)
Expect(T).ToStatic<'A' | 'B'>()
}
{
const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])])
const B = Type.TemplateLiteral([Type.Union([Type.Literal('A')])])
const T = Type.Extract(A, B)
Expect(T).ToStatic<'A'>()
}
// ------------------------------------------------------------------------
// TemplateLiteral | Union
// ------------------------------------------------------------------------
{
const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])])
const B = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])
const T = Type.Extract(A, B)
Expect(T).ToStatic<'A' | 'B' | 'C'>()
}
{
const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])])
const B = Type.Union([Type.Literal('A'), Type.Literal('B')])
const T = Type.Extract(A, B)
Expect(T).ToStatic<'A' | 'B'>()
}
{
const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])])
const B = Type.Union([Type.Literal('A')])
const T = Type.Extract(A, B)
Expect(T).ToStatic<'A'>()
}
// ------------------------------------------------------------------------
// Union | TemplateLiteral
// ------------------------------------------------------------------------
{
const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])
const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])])
const T = Type.Extract(A, B)
Expect(T).ToStatic<'A' | 'B' | 'C'>()
}
{
const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])
const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B')])])
const T = Type.Extract(A, B)
Expect(T).ToStatic<'A' | 'B'>()
}
{
const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])
const B = Type.TemplateLiteral([Type.Union([Type.Literal('A')])])
const T = Type.Extract(A, B)
Expect(T).ToStatic<'A'>()
}
// ------------------------------------------------------------------------
// Nested (Inference Test)
// ------------------------------------------------------------------------
{
const U = Type.Union([Type.Literal('A'), Type.Literal('B')])
const T = Type.Object({
type: Type.Extract(U, Type.Literal('A')),
})
Expect(T).ToStatic<{ type: 'A' }>()
}
{
const U = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])
const T = Type.Object({
type: Type.Extract(U, Type.Union([Type.Literal('A'), Type.Literal('B')])),
})
Expect(T).ToStatic<{ type: 'A' | 'B' }>()
}

54
test/static/function.ts Normal file
View File

@@ -0,0 +1,54 @@
import { Expect } from './assert'
import { Type } from '@sinclair/typebox'
{
// 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(T).ToStatic<(param_0: number, param_1: string) => { method: (param_0: number, param_1: string) => boolean }>()
}
{
// readonly-optional
const T = Type.Function([Type.ReadonlyOptional(Type.Array(Type.Number()))], Type.Number())
Expect(T).ToStaticDecode<(param_0?: readonly number[]) => number>()
}
{
// readonly
const T = Type.Function([Type.Readonly(Type.Array(Type.Number()))], Type.Number())
Expect(T).ToStaticDecode<(param_0: readonly number[]) => number>()
}
{
// optional 1
const T = Type.Function([Type.Optional(Type.Number())], Type.Number())
Expect(T).ToStaticDecode<(param_0?: number) => number>()
}
{
// optional 2
const T = Type.Function([Type.Number(), Type.Optional(Type.Number())], Type.Number())
Expect(T).ToStaticDecode<(param_0: number, param_1?: number) => number>()
}
const F = Type.Constructor([Type.Readonly(Type.Array(Type.String()))], Type.Number())
{
// 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>()
}

155
test/static/import.ts Normal file
View File

@@ -0,0 +1,155 @@
import { Expect } from './assert'
import { Type, Static } from '@sinclair/typebox'
// ------------------------------------------------------------------
// Enum 1
// ------------------------------------------------------------------
{
enum Enum {
A,
B,
}
const T = Type.Module({
T: Type.Object({
value: Type.Enum(Enum),
}),
}).Import('T')
Expect(T).ToStatic<{
value: Enum
}>()
}
// ------------------------------------------------------------------
// Enum 2
// ------------------------------------------------------------------
{
const T = Type.Module({
T: Type.Object({
value: Type.Enum({
x: 1,
y: 2,
}),
}),
}).Import('T')
Expect(T).ToStatic<{
value: 1 | 2
}>()
}
// ------------------------------------------------------------------
// Record 1
// ------------------------------------------------------------------
// prettier-ignore
{
const T = Type.Module({
R: Type.Object({ x: Type.Number(), y: Type.Number() }),
T: Type.Record(Type.String(), Type.Ref('R')),
}).Import('T')
type T = Static<typeof T>
Expect(T).ToStatic<{
[key: string]: { x: number, y: number }
}>()
}
// ------------------------------------------------------------------
// Record 2
// ------------------------------------------------------------------
// prettier-ignore
{
const T = Type.Module({
R: Type.Object({ x: Type.Number(), y: Type.Number() }),
T: Type.Record(Type.String(), Type.Partial(Type.Ref('R'))),
}).Import('T')
type T = Static<typeof T>
Expect(T).ToStatic<{
[key: string]: { x?: number, y?: number }
}>()
}
// ------------------------------------------------------------------
// Modifiers 1
// ------------------------------------------------------------------
// prettier-ignore
{
const Module = Type.Module({
T: Type.Object({
x: Type.ReadonlyOptional(Type.Null()),
y: Type.Readonly(Type.Null()),
z: Type.Optional(Type.Null()),
w: Type.Null()
})
})
const T = Module.Import('T')
type T = Static<typeof T>
Expect(T).ToStatic<{
readonly x?: null,
readonly y: null,
z?: null,
w: null
}>()
}
// ------------------------------------------------------------------
// Modifiers 2
// ------------------------------------------------------------------
// prettier-ignore
{
const Module = Type.Module({
T: Type.Object({
x: Type.ReadonlyOptional(Type.Array(Type.Null())),
y: Type.Readonly(Type.Array(Type.Null())),
z: Type.Optional(Type.Array(Type.Null())),
w: Type.Array(Type.Null())
})
})
const T = Module.Import('T')
type T = Static<typeof T>
Expect(T).ToStatic<{
readonly x?: null[],
readonly y: null[],
z?:null[],
w: null[]
}>()
}
// ------------------------------------------------------------------
// Modifiers 3
// ------------------------------------------------------------------
// prettier-ignore
{
const Module = Type.Module({
T: Type.Object({
x: Type.Array(Type.Null())
}),
// Computed Partial
U: Type.Partial(Type.Ref('T'))
})
const T = Module.Import('U')
type T = Static<typeof T>
Expect(T).ToStatic<{
x?: null[],
}>()
}
// ------------------------------------------------------------------
// Ref inside Recursive
// ------------------------------------------------------------------
// prettier-ignore
{
const Module = Type.Module({
T: Type.Recursive((_) =>
Type.Object({
M: Type.Ref("U"),
})
),
U: Type.Union([
Type.Literal("A"),
Type.Literal("B")
]),
});
const T = Module.Import("T");
type T = Static<typeof T>;
Expect(T).ToStatic<{
M: 'A'|'B'
}>();
}

58
test/static/index.ts Normal file
View File

@@ -0,0 +1,58 @@
import './any'
import './argument'
import './array'
import './async-iterator'
import './awaited'
import './bigint'
import './boolean'
import './capitalize'
import './composite'
import './const'
import './constructor-parameters'
import './constructor'
import './date'
import './deref'
import './enum'
import './extract'
import './exclude'
import './function'
import './import'
import './indexed'
import './instance-type'
import './intersect'
import './iterator'
import './keyof'
import './literal'
import './lowercase'
import './mapped'
import './modifier'
import './namespace'
import './never'
import './not'
import './null'
import './number'
import './object'
import './omit'
import './optional'
import './parameters'
import './partial'
import './pick'
import './readonly-optional'
import './readonly'
import './recursive'
import './record'
import './ref'
import './regexp'
import './required'
import './rest'
import './return-type'
import './string'
import './symbol'
import './syntax'
import './template-literal'
import './transform'
import './tuple'
import './uncapitalize'
import './union'
import './unknown'
import './uppercase'

276
test/static/indexed.ts Normal file
View File

@@ -0,0 +1,276 @@
import { Expect } from './assert'
import { Type, Static } from '@sinclair/typebox'
{
const T = Type.Object({
x: Type.Number(),
y: Type.String(),
})
const R = Type.Index(T, ['x', 'y'])
type O = Static<typeof R>
Expect(R).ToStatic<number | string>()
}
{
const T = Type.Tuple([Type.Number(), Type.String(), Type.Boolean()])
const R = Type.Index(T, Type.Union([Type.Literal('0'), Type.Literal('1')]))
type O = Static<typeof R>
Expect(R).ToStatic<number | string>()
}
{
const T = Type.Tuple([Type.Number(), Type.String(), Type.Boolean()])
const R = Type.Index(T, Type.Union([Type.Literal(0), Type.Literal(1)]))
type O = Static<typeof R>
Expect(R).ToStatic<number | string>()
}
{
const T = Type.Object({
ab: Type.Number(),
ac: Type.String(),
})
const R = Type.Index(T, Type.TemplateLiteral([Type.Literal('a'), Type.Union([Type.Literal('b'), Type.Literal('c')])]))
type O = Static<typeof R>
Expect(R).ToStatic<number | string>()
}
{
const A = Type.Tuple([Type.String(), Type.Boolean()])
const R = Type.Index(A, Type.Number())
type O = Static<typeof R>
Expect(R).ToStatic<string | boolean>()
}
{
const A = Type.Tuple([Type.String()])
const R = Type.Index(A, Type.Number())
type O = Static<typeof R>
Expect(R).ToStatic<string>()
}
{
const A = Type.Tuple([])
const R = Type.Index(A, Type.Number())
type O = Static<typeof R>
Expect(R).ToStaticNever()
}
{
const A = Type.Object({})
const R = Type.Index(A, Type.BigInt()) // Support Overload
type O = Static<typeof R>
Expect(R).ToStaticNever()
}
{
const A = Type.Array(Type.Number())
const R = Type.Index(A, Type.BigInt()) // Support Overload
type O = Static<typeof R>
Expect(R).ToStaticNever()
}
// ------------------------------------------------------------------
// Intersections
// ------------------------------------------------------------------
{
type A = { x: string; y: 1 }
type B = { x: string; y: number }
type C = A & B
type R = C['y']
const A = Type.Object({ x: Type.String(), y: Type.Literal(1) })
const B = Type.Object({ x: Type.String(), y: Type.Number() })
const C = Type.Intersect([A, B])
const R = Type.Index(C, ['y'])
type O = Static<typeof R>
Expect(R).ToStatic<1>()
}
{
type A = { x: string; y: 1 }
type B = { x: string; y: number }
type C = A & B
type R = C['x']
const A = Type.Object({ x: Type.String(), y: Type.Literal(1) })
const B = Type.Object({ x: Type.String(), y: Type.Number() })
const C = Type.Intersect([A, B])
const R = Type.Index(C, ['x'])
type O = Static<typeof R>
Expect(R).ToStatic<string>()
}
{
type A = { x: string; y: 1 }
type B = { x: string; y: number }
type C = A & B
type R = C['x' | 'y']
const A = Type.Object({ x: Type.String(), y: Type.Literal(1) })
const B = Type.Object({ x: Type.String(), y: Type.Number() })
const C = Type.Intersect([A, B])
const R = Type.Index(C, ['x', 'y'])
type O = Static<typeof R>
Expect(R).ToStatic<string | 1>()
}
{
type A = { x: string; y: number }
type B = { x: number; y: number }
type C = A & B
type R = C['x']
const A = Type.Object({ x: Type.String(), y: Type.Number() })
const B = Type.Object({ x: Type.Number(), y: Type.Number() })
const C = Type.Intersect([A, B])
const R = Type.Index(C, ['x'])
type O = Static<typeof R>
Expect(R).ToStaticNever()
}
{
type A = { x: string; y: number }
type B = { x: number; y: number }
type C = A & B
type R = C['y']
const A = Type.Object({ x: Type.String(), y: Type.Number() })
const B = Type.Object({ x: Type.Number(), y: Type.Number() })
const C = Type.Intersect([A, B])
const R = Type.Index(C, ['y'])
type O = Static<typeof R>
Expect(R).ToStatic<number>()
}
{
type A = { x: string; y: number }
type B = { x: number; y: number }
type C = A & B
type R = C['x' | 'y']
const A = Type.Object({ x: Type.String(), y: Type.Number() })
const B = Type.Object({ x: Type.Number(), y: Type.Number() })
const C = Type.Intersect([A, B])
const R = Type.Index(C, ['x', 'y'])
type O = Static<typeof R>
Expect(R).ToStatic<number>()
}
{
type A = { x: string; y: 1 }
type B = { x: string; y: number }
type C = { x: string; y: number }
type D = { x: string }
type I = (A & B) & (C & D)
type R = I['x' | 'y']
const A = Type.Object({ x: Type.String(), y: Type.Literal(1) })
const B = Type.Object({ x: Type.String(), y: Type.Number() })
const C = Type.Object({ x: Type.String(), y: Type.Number() })
const D = Type.Object({ x: Type.String() })
const I = Type.Intersect([Type.Intersect([A, B]), Type.Intersect([C, D])])
const R = Type.Index(I, ['x', 'y'])
type O = Static<typeof R>
Expect(R).ToStatic<string | 1>()
}
{
type A = { x: string; y: 1 }
type B = { x: number; y: number }
type C = { x: string; y: number }
type D = { x: string }
type I = (A & B) & (C & D)
type R = I['x' | 'y']
const A = Type.Object({ x: Type.String(), y: Type.Literal(1) })
const B = Type.Object({ x: Type.Number(), y: Type.Number() })
const C = Type.Object({ x: Type.String(), y: Type.Number() })
const D = Type.Object({ x: Type.String() })
const I = Type.Intersect([Type.Intersect([A, B]), Type.Intersect([C, D])])
const R = Type.Index(I, ['x', 'y'])
// TUnion<[TIntersect<[TIntersect<[TString, TNumber]>, TIntersect<[TString, TString]>]>, TIntersect<[TIntersect<[TLiteral<...>, TNumber]>, TNumber]>]>
// TUnion<[TUnion<[TString, TNumber, TString, TString]>, TUnion<[TLiteral<1>, TNumber, TNumber]>]>
type O = Static<typeof R>
Expect(R).ToStatic<1>()
}
{
type A = { x: string; y: 1 }
type B = { x: number; y: number }
type C = { x: string; y: number }
type D = { x: string }
type I = (A | B) & (C & D)
type R = I['x' | 'y']
const A = Type.Object({ x: Type.String(), y: Type.Literal(1) })
const B = Type.Object({ x: Type.Number(), y: Type.Number() })
const C = Type.Object({ x: Type.String(), y: Type.Number() })
const D = Type.Object({ x: Type.String() })
const I = Type.Intersect([Type.Union([A, B]), Type.Intersect([C, D])])
const R = Type.Index(I, ['x', 'y'])
// TUnion<[TIntersect<[TUnion<[TString, TNumber]>, TIntersect<[TString, TIntersect<[TString, TIntersect<[]>]>, TIntersect<[]>]>]>, TIntersect<...>]>
// TUnion<[TIntersect<[TUnion<[TString, TNumber]>, TIntersect<[TString, TString]>]>, TIntersect<[TUnion<[TLiteral<1>, TNumber]>, TNumber]>]>
// TUnion<[TIntersect<[TUnion<[TString, TNumber]>, TString, TString]>, TIntersect<[TUnion<[TLiteral<1>, TNumber]>, TNumber]>]>
type O = Static<typeof R>
Expect(R).ToStatic<number | string>()
}
{
type A = { x: 'A'; y: 1 }
type B = { x: 'B'; y: number }
type C = { x: 'C'; y: number }
type D = { x: 'D' }
type I = A | B | C | D
type R = I['x']
const A = Type.Object({ x: Type.Literal('A'), y: Type.Literal(1) })
const B = Type.Object({ x: Type.Literal('B'), y: Type.Number() })
const C = Type.Object({ x: Type.Literal('C'), y: Type.Number() })
const D = Type.Object({ x: Type.Literal('D') })
const I = Type.Union([A, B, C, D])
const R = Type.Index(I, ['x'])
type O = Static<typeof R>
Expect(R).ToStatic<'A' | 'B' | 'C' | 'D'>()
}
{
type I = {
x: string
y: number
z: I
}
type R = I['x' | 'y' | 'z']
const I = Type.Recursive((This) =>
Type.Object({
x: Type.String(),
y: Type.Number(),
z: This,
}),
)
const R = Type.Index(I, ['x', 'y', 'z']) // z unresolvable
type O = Static<typeof R>
Expect(R).ToStatic<string | number>()
}
// ------------------------------------------------
// Numeric | String Variants
// ------------------------------------------------
{
const T = Type.Object({
0: Type.Number(),
'1': Type.String(),
})
const R = Type.Index(T, [0, '1'])
type O = Static<typeof R>
Expect(R).ToStatic<string | number>()
}
{
const T = Type.Object({
'0': Type.Number(),
'1': Type.String(),
})
const R = Type.Index(T, Type.KeyOf(T))
type O = Static<typeof R>
Expect(R).ToStatic<string | number>()
}
{
const P = Type.Tuple([Type.Number(), Type.String()])
const R = Type.Object({
x: Type.Index(P, [0, 1]),
})
Expect(R).ToStatic<{ x: number | string }>()
}
{
const T = Type.Array(Type.String())
const I = Type.Index(T, Type.Number())
Expect(I).ToStatic<string>()
}
{
const T = Type.Array(Type.String())
const I = Type.Index(T, ['[number]'])
Expect(I).ToStatic<string>()
}

53
test/static/intersect.ts Normal file
View File

@@ -0,0 +1,53 @@
import { Expect } from './assert'
import { Type, Static } from '@sinclair/typebox'
{
const A = Type.Object({
A: Type.String(),
B: Type.String(),
})
const B = Type.Object({
X: Type.Number(),
Y: Type.Number(),
})
const T = Type.Intersect([A, B])
Expect(T).ToStatic<
{
A: string
B: string
} & {
X: number
Y: number
}
>()
}
{
const A = Type.Object({
A: Type.Optional(Type.String()),
})
const B = Type.Object({
B: Type.String(),
})
const T = Type.Intersect([A, B])
Expect(T).ToStatic<{ A?: string | undefined } & { B: string }>()
}
// https://github.com/sinclairzx81/typebox/issues/113
// https://github.com/sinclairzx81/typebox/issues/187
{
const A = Type.Object({ A: Type.String() })
const B = Type.Object({ B: Type.String() })
const C = Type.Object({ C: Type.String() })
const T = Type.Intersect([A, Type.Union([B, C])])
type T = Static<typeof T>
const _0: T = { A: '', B: '' }
const _1: T = { A: '', C: '' }
const _3: T = { A: '', B: '', C: '' }
// invert equivelence (expect true both cases)
type T1 = T extends { A: string } & ({ B: string } | { C: string }) ? true : false
type T2 = { A: string } & ({ B: string } | { C: string }) extends T ? true : false
Expect(T).ToStatic<{ A: string } & ({ B: string } | { C: string })>() // solved!
}

4
test/static/iterator.ts Normal file
View File

@@ -0,0 +1,4 @@
import { Expect } from './assert'
import { Type } from '@sinclair/typebox'
Expect(Type.Iterator(Type.String())).ToStatic<IterableIterator<string>>()

102
test/static/keyof.ts Normal file
View File

@@ -0,0 +1,102 @@
import { Expect } from './assert'
import { Type } from '@sinclair/typebox'
{
const K = Type.KeyOf(
Type.Object({
A: Type.Null(),
B: Type.Null(),
C: Type.Null(),
}),
)
Expect(K).ToStatic<'A' | 'B' | 'C'>()
}
{
const T = Type.Pick(
Type.Object({
A: Type.Null(),
B: Type.Null(),
C: Type.Null(),
}),
['A', 'B'],
)
const K = Type.KeyOf(T)
Expect(K).ToStatic<'A' | 'B'>()
}
{
const T = Type.Omit(
Type.Object({
A: Type.Null(),
B: Type.Null(),
C: Type.Null(),
}),
['A', 'B'],
)
const K = Type.KeyOf(T)
Expect(K).ToStatic<'C'>()
}
{
const T = Type.KeyOf(
Type.Omit(
Type.Object({
A: Type.Null(),
B: Type.Null(),
C: Type.Null(),
}),
['A', 'B'],
),
)
Expect(T).ToStatic<'C'>()
}
{
{
const A = Type.Object({ type: Type.Literal('A') })
const B = Type.Object({ type: Type.Literal('B') })
const C = Type.Object({ type: Type.Literal('C') })
const Union = Type.Union([A, B, C])
const Extended = Type.Object({
x: Type.Number(),
y: Type.Number(),
z: Type.Number(),
})
const T = Type.Intersect([Union, Extended])
const K1 = Type.KeyOf(T)
Expect(K1).ToStatic<'type' | 'x' | 'y' | 'z'>()
const P = Type.Omit(T, ['type', 'x'])
const K2 = Type.KeyOf(P)
Expect(K2).ToStatic<'y' | 'z'>()
}
}
{
const T = Type.Recursive((Self) =>
Type.Object({
a: Type.String(),
b: Type.String(),
c: Type.String(),
d: Type.Array(Self),
}),
)
const K = Type.KeyOf(T)
Expect(K).ToStatic<'a' | 'b' | 'c' | 'd'>()
}
{
const T = Type.Object({
a: Type.Optional(Type.String()),
b: Type.Optional(Type.String()),
c: Type.Optional(Type.String()),
})
const K = Type.KeyOf(T)
Expect(K).ToStatic<'a' | 'b' | 'c'>()
}

8
test/static/literal.ts Normal file
View File

@@ -0,0 +1,8 @@
import { Expect } from './assert'
import { Type } from '@sinclair/typebox'
Expect(Type.Literal('hello')).ToStatic<'hello'>()
Expect(Type.Literal(true)).ToStatic<true>()
Expect(Type.Literal(42)).ToStatic<42>()

14
test/static/lowercase.ts Normal file
View File

@@ -0,0 +1,14 @@
import { Expect } from './assert'
import { Type } from '@sinclair/typebox'
Expect(Type.Lowercase(Type.Literal('HELLO'))).ToStatic<'hello'>()
Expect(Type.Lowercase(Type.Union([Type.Literal('HELLO'), Type.Literal('WORLD')]))).ToStatic<'hello' | 'world'>()
Expect(Type.Lowercase(Type.TemplateLiteral('HELLO${0|1}'))).ToStatic<'hello0' | 'hello1'>()
// prettier-ignore
Expect(Type.Lowercase(Type.TemplateLiteral([Type.Literal('HELLO'), Type.Union([Type.Literal(1), Type.Literal(2)])]))).ToStatic<'hello1' | 'hello2'>()
// passthrough
Expect(Type.Lowercase(Type.Object({ x: Type.Number() }))).ToStatic<{ x: number }>()

417
test/static/mapped.ts Normal file
View File

@@ -0,0 +1,417 @@
import { Expect } from './assert'
import { Static, Type } from '@sinclair/typebox'
// prettier-ignore
{ // Generative
const A = Type.Mapped(Type.Union([
Type.Literal('x'),
Type.Literal('y'),
Type.Literal('z'),
]), K => Type.Number())
Expect(A).ToStatic<{
x: number,
y: number,
z: number
}>
const B = Type.Mapped(Type.TemplateLiteral('${0|1}${0|1}'), K => Type.Number())
Expect(B).ToStatic<{
'00': number,
'01': number,
'10': number,
'11': number,
}>
}
// prettier-ignore
{ // Generative Nested
const T = Type.Mapped(Type.TemplateLiteral('${a|b}'), X =>
Type.Mapped(Type.TemplateLiteral('${c|d}'), Y =>
Type.Mapped(Type.TemplateLiteral('${e|f}'), Z =>
Type.Tuple([X, Y, Z])
)
)
)
type E = {
[X in `${'a' | 'b'}`]: {
[Y in `${'c' | 'd'}`]: {
[Z in `${'e' | 'f'}`]: [X, Y, Z]
}
}
}
Expect(T).ToStatic<E> // ok
}
// prettier-ignore
{ // Identity
const T = Type.Object({
x: Type.Number(),
y: Type.String(),
z: Type.Boolean()
})
const A = Type.Mapped(Type.KeyOf(T), K => K)
Expect(A).ToStatic<{
x: 'x',
y: 'y',
z: 'z'
}>()
const B = Type.Mapped(Type.KeyOf(T), K => Type.Index(T, K))
Expect(B).ToStatic<{
x: number,
y: string,
z: boolean
}>()
}
// prettier-ignore
{ // Extract
const T = Type.Object({
x: Type.Union([Type.String(), Type.Number(), Type.Boolean()])
})
const A = Type.Mapped(Type.KeyOf(T), K => {
return Type.Extract(Type.Index(T, K), Type.String())
})
Expect(A).ToStatic<{
x: string
}>
const B = Type.Mapped(Type.KeyOf(T), K => {
return Type.Extract(Type.Index(T, K), Type.Union([
Type.String(),
Type.Number()
]))
})
Expect(B).ToStatic<{
x: string | number
}>
const C = Type.Mapped(Type.KeyOf(T), K => {
return Type.Extract(Type.Index(T, K), Type.Null())
})
Expect(C).ToStatic<{
x: never
}>
}
// prettier-ignore
{ // Numeric Keys
const T = Type.Object({
0: Type.Number(),
1: Type.Number(),
2: Type.Number()
})
const A = Type.Mapped(Type.KeyOf(T), K => Type.Index(T, K))
Expect(A).ToStatic<{
0: number,
1: number,
2: number
}>
}
// prettier-ignore
{ // Extends
const T = Type.Object({
x: Type.Number(),
y: Type.String(),
z: Type.Boolean()
})
const A = Type.Mapped(Type.KeyOf(T), K => {
return (
Type.Extends(K, Type.Literal('x'), Type.Literal(1),
Type.Extends(K, Type.Literal('y'), Type.Literal(2),
Type.Extends(K, Type.Literal('z'), Type.Literal(3), Type.Never())))
)
})
Expect(A).ToStatic<{
x: 1,
y: 2,
z: 3
}>
const B = Type.Mapped(Type.KeyOf(T), K => {
return (
Type.Extends(Type.Index(T, K), Type.Number(), Type.Literal(3),
Type.Extends(Type.Index(T, K), Type.String(), Type.Literal(2),
Type.Extends(Type.Index(T, K), Type.Boolean(), Type.Literal(1), Type.Never())))
)
})
Expect(B).ToStatic<{
x: 3,
y: 2,
z: 1
}>
}
// prettier-ignore
{ // Exclude
const T = Type.Object({
x: Type.Union([Type.String(), Type.Number(), Type.Boolean()])
})
const A = Type.Mapped(Type.KeyOf(T), K => {
return Type.Exclude(Type.Index(T, K), Type.String())
})
Expect(A).ToStatic<{
x: number | boolean
}>
const B = Type.Mapped(Type.KeyOf(T), K => {
return Type.Exclude(Type.Index(T, K), Type.Union([
Type.String(),
Type.Number()
]))
})
Expect(B).ToStatic<{
x: boolean
}>
const C = Type.Mapped(Type.KeyOf(T), K => {
return Type.Exclude(Type.Index(T, K), Type.Null())
})
Expect(C).ToStatic<{
x: string | number | boolean
}>
}
// prettier-ignore
{ // Non-Evaluated Indexed
const T = Type.Object({
x: Type.Number()
})
const A = Type.Mapped(Type.KeyOf(T), K => Type.Array(Type.Index(T, K)))
Expect(A).ToStatic<{ x: number[] }>
const B = Type.Mapped(Type.KeyOf(T), K => Type.Promise(Type.Index(T, K)))
Expect(B).ToStatic<{ x: Promise<number> }>
const C = Type.Mapped(Type.KeyOf(T), K => Type.Function([Type.Index(T, K)], Type.Index(T, K)))
Expect(C).ToStatic<{ x: (x: number) => number }>
const D = Type.Mapped(Type.KeyOf(T), K => Type.Tuple([Type.Index(T, K), Type.Index(T, K)]))
Expect(D).ToStatic<{ x: [number, number] }>
const E = Type.Mapped(Type.KeyOf(T), K => Type.Union([Type.Index(T, K)]))
Expect(E).ToStatic<{ x: number }>
const F = Type.Mapped(Type.KeyOf(T), K => Type.Intersect([Type.Index(T, K)]))
Expect(F).ToStatic<{ x: number }>
}
// prettier-ignore
{ // Modifiers
const T = Type.Object({
x: Type.Optional(Type.Number()),
y: Type.Number()
})
// Additive
const A = Type.Mapped(Type.KeyOf(T), K => Type.Optional(Type.Index(T, K), true))
Expect(A).ToStatic<{ x?: number, y?: number}>()
// Subtractive
const S = Type.Mapped(Type.KeyOf(T), K => Type.Optional(Type.Index(T, K), false))
Expect(S).ToStatic<{ x: number, y: number}>()
}
// prettier-ignore
{ // Modifiers
const T = Type.Object({
x: Type.Readonly(Type.Number()),
y: Type.Number()
})
// Additive
const A = Type.Mapped(Type.KeyOf(T), K => Type.Readonly(Type.Index(T, K), true))
Expect(A).ToStatic<{ readonly x: number, readonly y: number}>()
// Subtractive
const S = Type.Mapped(Type.KeyOf(T), K => Type.Readonly(Type.Index(T, K), false))
Expect(S).ToStatic<{ x: number, y: number}>()
}
// ------------------------------------------------------------------
// Finite Boolean
// ------------------------------------------------------------------
{
const T = Type.TemplateLiteral('${boolean}')
const M = Type.Mapped(T, (K) => K)
Expect(M).ToStatic<{
true: 'true'
false: 'false'
}>
}
{
const T = Type.TemplateLiteral('${0|1}${boolean}')
const M = Type.Mapped(T, (K) => K)
Expect(M).ToStatic<{
'0true': '0true'
'0false': '0false'
'1true': '1true'
'1false': '1false'
}>
}
{
const T = Type.TemplateLiteral('${boolean}${0|1}')
const M = Type.Mapped(T, (K) => K)
Expect(M).ToStatic<{
true0: 'true0'
false0: 'false0'
true1: 'true1'
false1: 'false1'
}>
}
{
const T = Type.TemplateLiteral([Type.Union([Type.Literal(0), Type.Literal(1)]), Type.Union([Type.Literal(0), Type.Literal(1)])])
const M = Type.Mapped(T, (K) => K)
Expect(M).ToStatic<{
'00': '00'
'01': '01'
'10': '10'
'11': '11'
}>
}
{
const T = Type.Object({
hello: Type.Number(),
world: Type.String(),
})
const M = Type.Mapped(Type.Uppercase(Type.KeyOf(T)), (K) => {
return Type.Index(T, Type.Lowercase(K))
})
Expect(M).ToStatic<{
HELLO: number
WORLD: string
}>
}
// ------------------------------------------------------------------
// Interior Partial
// ------------------------------------------------------------------
{
const T = Type.Object({
x: Type.Object({
x: Type.Number(),
y: Type.Number(),
}),
y: Type.Object({
x: Type.Number(),
y: Type.Number(),
}),
})
const M = Type.Mapped(Type.KeyOf(T), (K) => {
return Type.Partial(Type.Index(T, K))
})
Expect(M).ToStatic<{
x: { x?: number; y?: number }
y: { x?: number; y?: number }
}>
}
// ------------------------------------------------------------------
// Interior Required
// ------------------------------------------------------------------
{
const T = Type.Object({
x: Type.Partial(
Type.Object({
x: Type.Number(),
y: Type.Number(),
}),
),
y: Type.Partial(
Type.Object({
x: Type.Number(),
y: Type.Number(),
}),
),
})
const M = Type.Mapped(Type.KeyOf(T), (K) => {
return Type.Required(Type.Index(T, K))
})
Expect(M).ToStatic<{
x: { x: number; y: number }
y: { x: number; y: number }
}>
}
// ------------------------------------------------------------------
// Pick With Key
// ------------------------------------------------------------------
// prettier-ignore
{
const T = Type.Object({
x: Type.Object({
x: Type.Number(),
y: Type.Number()
}),
y: Type.Object({
x: Type.Number(),
y: Type.Number()
})
})
const M = Type.Mapped(Type.KeyOf(T), K => {
return Type.Pick(T, K)
})
Expect(M).ToStatic<{
x: { x: { x: number; y: number; }; };
y: { y: { x: number; y: number; }; };
}>
}
// ------------------------------------------------------------------
// Pick With Result
// ------------------------------------------------------------------
{
const T = Type.Object({
x: Type.Object({
x: Type.Number(),
y: Type.Number(),
}),
y: Type.Object({
x: Type.Number(),
y: Type.Number(),
}),
})
const M = Type.Mapped(Type.KeyOf(T), (K) => {
return Type.Pick(Type.Index(T, K), ['x'])
})
Expect(M).ToStatic<{
x: { x: number }
y: { x: number }
}>
}
// ------------------------------------------------------------------
// Omit With Key
// ------------------------------------------------------------------
// prettier-ignore
{
const T = Type.Object({
x: Type.Object({
x: Type.Number(),
y: Type.Number()
}),
y: Type.Object({
x: Type.Number(),
y: Type.Number()
})
})
const M = Type.Mapped(Type.KeyOf(T), K => {
return Type.Omit(T, K)
})
Expect(M).ToStatic<{
x: { y: { x: number; y: number; }; };
y: { x: { x: number; y: number; }; };
}>
}
// ------------------------------------------------------------------
// Omit With Result
// ------------------------------------------------------------------
{
const T = Type.Object({
x: Type.Object({
x: Type.Number(),
y: Type.Number(),
}),
y: Type.Object({
x: Type.Number(),
y: Type.Number(),
}),
})
const M = Type.Mapped(Type.KeyOf(T), (K) => {
return Type.Omit(Type.Index(T, K), ['x'])
})
Expect(M).ToStatic<{
x: { y: number }
y: { y: number }
}>
}
// ------------------------------------------------------------------
// With Enum
// issue: https://github.com/sinclairzx81/typebox/issues/897
// ------------------------------------------------------------------
{
enum E {
A,
B,
}
const T = Type.Object({ a: Type.Enum(E) })
const M = Type.Mapped(Type.KeyOf(T), (K) => Type.Index(T, K))
Expect(M).ToStatic<{ a: E }>
}

18
test/static/modifier.ts Normal file
View File

@@ -0,0 +1,18 @@
import { Expect } from './assert'
import { Type, TSchema } from '@sinclair/typebox'
// Asserts combinatory modifiers
{
const T = Type.Object({
A: Type.ReadonlyOptional(Type.String()),
B: Type.Readonly(Type.String()),
C: Type.Optional(Type.String()),
D: Type.String(),
})
Expect(T).ToStatic<{
readonly A?: string
readonly B: string
C?: string
D: string
}>()
}

7
test/static/never.ts Normal file
View File

@@ -0,0 +1,7 @@
import { Expect } from './assert'
import { Type } from '@sinclair/typebox'
{
const T = Type.Never()
Expect(T).ToStaticNever()
}

26
test/static/not.ts Normal file
View File

@@ -0,0 +1,26 @@
import { Expect } from './assert'
import { Type } from '@sinclair/typebox'
{
// -------------------------------------------------------------------------
// Issue: type T = number extends not number ? true : false // true
// type T = number extends unknown ? true : false // true
//
// TypeScript does not support type negation. The best TypeBox can do is
// treat "not" as "unknown". From this standpoint, the extends assignability
// check needs to return true for the following case to keep TypeBox aligned
// with TypeScript static inference.
// -------------------------------------------------------------------------
const A = Type.Number()
const B = Type.Not(Type.Number())
const T = Type.Extends(A, B, Type.Literal(true), Type.Literal(false))
Expect(T).ToStatic<true>()
}
{
const T = Type.Not(Type.Number())
Expect(T).ToStatic<unknown>()
}
{
const T = Type.Not(Type.Not(Type.Number()))
Expect(T).ToStatic<number>()
}

4
test/static/null.ts Normal file
View File

@@ -0,0 +1,4 @@
import { Expect } from './assert'
import { Type } from '@sinclair/typebox'
Expect(Type.Null()).ToStatic<null>()

4
test/static/number.ts Normal file
View File

@@ -0,0 +1,4 @@
import { Expect } from './assert'
import { Type } from '@sinclair/typebox'
Expect(Type.Number()).ToStatic<number>()

70
test/static/object.ts Normal file
View File

@@ -0,0 +1,70 @@
import { Expect } from './assert'
import { Type } from '@sinclair/typebox'
{
const T = Type.Object({
A: Type.String(),
B: Type.String(),
C: Type.String(),
})
Expect(T).ToStatic<{
A: string
B: string
C: string
}>()
}
{
const T = Type.Object({
A: Type.Object({
A: Type.String(),
B: Type.String(),
C: Type.String(),
}),
B: Type.Object({
A: Type.String(),
B: Type.String(),
C: Type.String(),
}),
C: Type.Object({
A: Type.String(),
B: Type.String(),
C: Type.String(),
}),
})
Expect(T).ToStatic<{
A: {
A: string
B: string
C: string
}
B: {
A: string
B: string
C: string
}
C: {
A: string
B: string
C: string
}
}>()
}
{
const T = Type.Object(
{
A: Type.Number(),
B: Type.Number(),
C: Type.Number(),
},
{
additionalProperties: Type.Boolean(),
},
)
// note: Pending TypeScript support for negated types.
Expect(T).ToStatic<{
A: number
B: number
C: number
}>()
}

104
test/static/omit.ts Normal file
View File

@@ -0,0 +1,104 @@
import { Expect } from './assert'
import { Type, Static } from '@sinclair/typebox'
{
const A = Type.Object({
A: Type.String(),
B: Type.String(),
C: Type.String(),
})
const T = Type.Omit(A, ['A', 'B'])
type T = Static<typeof T>
Expect(T).ToStatic<{
C: string
}>()
}
{
const A = Type.Object({
A: Type.String(),
B: Type.String(),
C: Type.String(),
})
const keys = ['A', 'B'] as const
const T = Type.Omit(A, keys)
type T = Static<typeof T>
Expect(T).ToStatic<{
C: string
}>()
}
{
const A = Type.Object({
A: Type.String(),
B: Type.String(),
C: Type.String(),
})
const B = Type.Object({
A: Type.String(),
B: Type.String(),
})
const T = Type.Omit(A, Type.KeyOf(B))
type T = Static<typeof T>
Expect(T).ToStatic<{
C: string
}>()
}
{
const A = Type.Object({ type: Type.Literal('A') })
const B = Type.Object({ type: Type.Literal('B') })
const C = Type.Object({ type: Type.Literal('C') })
const Union = Type.Union([A, B, C])
const Extended = Type.Object({
x: Type.Number(),
y: Type.Number(),
z: Type.Number(),
})
const T = Type.Intersect([Union, Extended])
Expect(T).ToStatic<
(
| {
type: 'A'
}
| {
type: 'B'
}
| {
type: 'C'
}
) & {
x: number
y: number
z: number
}
>()
const P = Type.Omit(T, ['type', 'x'])
Expect(P).ToStatic<
({} | {} | {}) & {
y: number
z: number
}
>()
const O = Type.Partial(P)
Expect(O).ToStatic<
({} | {} | {}) & {
y?: number | undefined
z?: number | undefined
}
>()
}

49
test/static/optional.ts Normal file
View File

@@ -0,0 +1,49 @@
import { Expect } from './assert'
import { Type, Static } from '@sinclair/typebox'
{
const T = Type.Object({
A: Type.Optional(Type.String()),
})
type T = Static<typeof T>
Expect(T).ToStatic<{
A?: string
}>()
}
// Noop
// prettier-ignore
{
const T = Type.Object({
A: Type.Optional(Type.String(), false),
})
type T = Static<typeof T>
Expect(T).ToStatic<{
A: string
}>()
}
// Additive
// prettier-ignore
{
const T = Type.Object({
A: Type.Optional(Type.String(), true),
})
type T = Static<typeof T>
Expect(T).ToStatic<{
A?: string
}>()
}
// Subtractive
// prettier-ignore
{
const T = Type.Object({
A: Type.Optional(Type.Optional(Type.String()), false)
})
type T = Static<typeof T>
Expect(T).ToStatic<{
A: string
}>()
}

13
test/static/parameters.ts Normal file
View File

@@ -0,0 +1,13 @@
import { Expect } from './assert'
import { Type } from '@sinclair/typebox'
const C = Type.Function(
[Type.Number(), Type.String()],
Type.Object({
method: Type.Function([Type.Number(), Type.String()], Type.Boolean()),
}),
)
const P = Type.Parameters(C)
Expect(P).ToStatic<[number, string]>()

105
test/static/partial.ts Normal file
View File

@@ -0,0 +1,105 @@
import { Expect } from './assert'
import { Type, Static } from '@sinclair/typebox'
import * as Types from '@sinclair/typebox'
{
const T = Type.Partial(
Type.Object({
A: Type.String(),
B: Type.String(),
C: Type.String(),
}),
)
type T = Static<typeof T>
Expect(T).ToStatic<{
A?: string
B?: string
C?: string
}>()
}
{
{
const A = Type.Object({ type: Type.Literal('A') })
const B = Type.Object({ type: Type.Literal('B') })
const C = Type.Object({ type: Type.Literal('C') })
const Union = Type.Union([A, B, C])
const Extended = Type.Object({
x: Type.Number(),
y: Type.Number(),
z: Type.Number(),
})
const T = Type.Intersect([Union, Extended])
Expect(T).ToStatic<
(
| {
type: 'A'
}
| {
type: 'B'
}
| {
type: 'C'
}
) & {
x: number
y: number
z: number
}
>()
const P = Type.Partial(T)
Expect(P).ToStatic<
(
| {
type?: 'A' | undefined
}
| {
type?: 'B' | undefined
}
| {
type?: 'C' | undefined
}
) & {
x?: number | undefined
y?: number | undefined
z?: number | undefined
}
>()
}
}
{
// https://github.com/sinclairzx81/typebox/issues/655
const T = Type.Object({
a: Type.ReadonlyOptional(Type.Number()),
b: Type.Readonly(Type.Number()),
c: Type.Optional(Type.Number()),
d: Type.Number(),
})
const R: Types.TObject<{
a: Types.TReadonlyOptional<Types.TNumber>
b: Types.TReadonlyOptional<Types.TNumber>
c: Types.TOptional<Types.TNumber>
d: Types.TOptional<Types.TNumber>
}> = Type.Partial(T)
}
// ------------------------------------------------------------------
// Intrinsic Passthough
// https://github.com/sinclairzx81/typebox/issues/1169
// ------------------------------------------------------------------
// prettier-ignore
{
const T = Type.Partial(Type.Union([Type.Number(), Type.Object({
x: Type.Number()
})]))
Expect(T).ToStatic<number | { x?: number }>
}
// prettier-ignore
{
const T = Type.Partial(Type.Union([Type.Literal(1), Type.Object({
x: Type.Number()
})]))
Expect(T).ToStatic<1 | { x?: number }>
}

129
test/static/pick.ts Normal file
View File

@@ -0,0 +1,129 @@
import { Expect } from './assert'
import { Type, Static } from '@sinclair/typebox'
{
const A = Type.Object({
A: Type.String(),
B: Type.String(),
C: Type.String(),
})
const T = Type.Pick(A, ['A', 'B'])
type T = Static<typeof T>
Expect(T).ToStatic<{
A: string
B: string
}>()
}
{
const A = Type.Object({
A: Type.String(),
B: Type.String(),
C: Type.String(),
})
const keys = ['A', 'B'] as const
const T = Type.Pick(A, ['A', 'B'])
type T = Static<typeof T>
Expect(T).ToStatic<{
A: string
B: string
}>()
}
{
const A = Type.Object({
A: Type.String(),
B: Type.String(),
C: Type.String(),
})
const B = Type.Object({
A: Type.String(),
B: Type.String(),
})
const T = Type.Pick(A, Type.KeyOf(B))
type T = Static<typeof T>
Expect(T).ToStatic<{
A: string
B: string
}>()
}
{
const A = Type.Object({ type: Type.Literal('A') })
const B = Type.Object({ type: Type.Literal('B') })
const C = Type.Object({ type: Type.Literal('C') })
const Union = Type.Union([A, B, C])
const Extended = Type.Object({
x: Type.Number(),
y: Type.Number(),
z: Type.Number(),
})
const T = Type.Intersect([Union, Extended])
Expect(T).ToStatic<
(
| {
type: 'A'
}
| {
type: 'B'
}
| {
type: 'C'
}
) & {
x: number
y: number
z: number
}
>()
const K = Type.KeyOf(T)
Expect(K).ToStatic<'type' | 'x' | 'y' | 'z'>()
const P = Type.Pick(T, ['type', 'x'])
Expect(P).ToStatic<
(
| {
type: 'A'
}
| {
type: 'B'
}
| {
type: 'C'
}
) & {
x: number
}
>()
const O = Type.Partial(P)
Expect(O).ToStatic<
(
| {
type?: 'A' | undefined
}
| {
type?: 'B' | undefined
}
| {
type?: 'C' | undefined
}
) & {
x?: number | undefined
}
>()
}

View File

@@ -0,0 +1,28 @@
import { Expect } from './assert'
import { Type, TSchema, TReadonlyOptional } from '@sinclair/typebox'
{
const T = Type.Object({
A: Type.ReadonlyOptional(Type.String()),
})
Expect(T).ToStatic<{
readonly A?: string
}>()
}
{
const T = Type.ReadonlyOptional(Type.String())
function test(_: TReadonlyOptional<TSchema>) {}
test(T)
}
{
const T = Type.Readonly(Type.String())
function test(_: TReadonlyOptional<TSchema>) {}
// @ts-expect-error
test(T)
}
{
const T = Type.Optional(Type.String())
function test(_: TReadonlyOptional<TSchema>) {}
// @ts-expect-error
test(T)
}

50
test/static/readonly.ts Normal file
View File

@@ -0,0 +1,50 @@
import { Expect } from './assert'
import { Type, Static } from '@sinclair/typebox'
{
const T = Type.Object({
A: Type.Readonly(Type.String()),
})
type T = Static<typeof T>
Expect(T).ToStatic<{
readonly A: string
}>()
}
// Noop
// prettier-ignore
{
const T = Type.Object({
A: Type.Readonly(Type.String(), false),
})
type T = Static<typeof T>
Expect(T).ToStatic<{
A: string
}>()
}
// Additive
// prettier-ignore
{
const T = Type.Object({
A: Type.Readonly(Type.String(), true),
})
type T = Static<typeof T>
Expect(T).ToStatic<{
readonly A: string
}>()
}
// Subtractive
// prettier-ignore
{
const T = Type.Object({
A: Type.Readonly(Type.Readonly(Type.String()), false)
})
type T = Static<typeof T>
Expect(T).ToStatic<{
A: string
}>()
}

257
test/static/record.ts Normal file
View File

@@ -0,0 +1,257 @@
import { Expect } from './assert'
import { Type, Static } from '@sinclair/typebox'
{
// type K = string
const K = Type.String()
const T = Type.Record(K, Type.Number())
type T = Static<typeof T>
Expect(T).ToStatic<Record<string, number>>()
}
{
// type K = string
const K = Type.RegExp(/foo|bar/)
const T = Type.Record(K, Type.Number())
type T = Static<typeof T>
Expect(T).ToStatic<Record<string, number>>()
}
{
// type K = number
const K = Type.Number()
const T = Type.Record(K, Type.Number())
type T = Static<typeof T>
Expect(T).ToStatic<Record<number, number>>()
}
{
// type K = 'A' | 'B' | 'C'
const K = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])
const T = Type.Record(K, Type.Number())
type T = Static<typeof T>
Expect(T).ToStatic<Record<'A' | 'B' | 'C', number>>()
}
{
// type K = keyof { A: number, B: number, C: number }
const K = Type.KeyOf(
Type.Object({
A: Type.Number(),
B: Type.Number(),
C: Type.Number(),
}),
)
const T = Type.Record(K, Type.Number())
type T = Static<typeof T>
Expect(T).ToStatic<Record<'A' | 'B' | 'C', number>>()
}
{
// type K = keyof Omit<{ A: number, B: number, C: number }, 'C'>
const K = Type.KeyOf(
Type.Omit(
Type.Object({
A: Type.Number(),
B: Type.Number(),
C: Type.Number(),
}),
['C'],
),
)
const T = Type.Record(K, Type.Number())
type T = Static<typeof T>
Expect(T).ToStatic<Record<'A' | 'B', number>>()
}
{
const T = Type.Record(Type.Number(), Type.String())
Expect(T).ToStatic<Record<number, string>>()
}
{
const T = Type.Record(Type.Integer(), Type.String())
Expect(T).ToStatic<Record<number, string>>()
}
{
// Should support enum keys 1
enum E {
A = 'X',
B = 'Y',
C = 'Z',
}
const T = Type.Record(Type.Enum(E), Type.Number())
Expect(T).ToStatic<{
X: number
Y: number
Z: number
}>()
}
{
// Should support enum keys 2
enum E {
A,
B,
C,
}
const T = Type.Record(Type.Enum(E), Type.Number())
Expect(T).ToStatic<{
0: number
1: number
2: number
}>()
}
{
// Should support enum keys 3
enum E {
A = 1,
B = '2',
C = 'Z',
}
const T = Type.Record(Type.Enum(E), Type.Number())
Expect(T).ToStatic<{
1: number
2: number
Z: number
}>()
}
{
// should support infinite record keys
// https://github.com/sinclairzx81/typebox/issues/604
const K = Type.TemplateLiteral('key${number}')
const R = Type.Record(K, Type.Number())
Expect(R).ToStatic<Record<`key${number}`, number>>()
}
{
// should support infinite record keys with intersect
// https://github.com/sinclairzx81/typebox/issues/604
const K = Type.TemplateLiteral('key${number}')
const R = Type.Record(K, Type.Number())
const T = Type.Object({ x: Type.Number(), y: Type.Number() })
const I = Type.Intersect([R, T])
Expect(I).ToStatic<Record<`key${number}`, number> & { x: number; y: number }>()
}
{
// expect T as Object
enum E {
A,
B,
C,
}
const T = Type.Record(Type.Enum(E), Type.Number())
Expect(T).ToStatic<{
0: number
1: number
2: number
}>
}
{
// expect T as Partial Object
enum E {
A,
B,
C,
}
const T = Type.Partial(Type.Record(Type.Enum(E), Type.Number()))
Expect(T).ToStatic<{
0?: number
1?: number
2?: number
}>
}
{
// expect T to support named properties
enum E {
A = 'A',
B = 'B',
C = 'C',
}
const T = Type.Record(Type.Enum(E), Type.Number())
Expect(T).ToStatic<{
A: number
B: number
C: number
}>
}
{
// expect T to support named properties
enum E {}
const T = Type.Record(Type.Enum(E), Type.Number())
Expect(T).ToStatic<{ [x: string]: number }>
}
// ------------------------------------------------------------------
// Dollar Sign Escape
// https://github.com/sinclairzx81/typebox/issues/794
// ------------------------------------------------------------------
// prettier-ignore
{
const K = Type.TemplateLiteral('$prop${A|B|C}') // issue
const T = Type.Record(K, Type.String())
Expect(T).ToStatic<{
'$propA': string,
'$propB': string,
'$propC': string
}>()
}
// ------------------------------------------------------------------
// https://github.com/sinclairzx81/typebox/issues/916
// ------------------------------------------------------------------
{
const K = Type.Any()
const T = Type.Record(K, Type.String())
Expect(T).ToStatic<Record<string, string>>()
}
{
const K = Type.Never()
const T = Type.Record(K, Type.String())
Expect(T).ToStatic<{}>()
}
// ------------------------------------------------------------------
// Deep Union
// https://github.com/sinclairzx81/typebox/issues/1208
// ------------------------------------------------------------------
// prettier-ignore
{
const A = Type.Record(Type.Union([
Type.Literal(0), Type.Literal(1), Type.Literal(2), Type.Literal(3), Type.Literal(4), Type.Literal(5), Type.Literal(6), Type.Literal(7),
Type.Literal(8), Type.Literal(9), Type.Literal(10), Type.Literal(11), Type.Literal(12), Type.Literal(13), Type.Literal(14), Type.Literal(15),
Type.Literal(16), Type.Literal(17), Type.Literal(18), Type.Literal(19), Type.Literal(20), Type.Literal(21), Type.Literal(22), Type.Literal(23),
Type.Literal(24), Type.Literal(25), Type.Literal(26), Type.Literal(27), Type.Literal(28), Type.Literal(29), Type.Literal(30), Type.Literal(31),
Type.Literal(32), Type.Literal(33), Type.Literal(34), Type.Literal(35), Type.Literal(36), Type.Literal(37), Type.Literal(38), Type.Literal(39),
Type.Literal(40), Type.Literal(41), Type.Literal(42), Type.Literal(43), Type.Literal(44), Type.Literal(45), Type.Literal(46), Type.Literal(47),
Type.Literal(48), Type.Literal(49), Type.Literal(50), Type.Literal(51), Type.Literal(52), Type.Literal(53), Type.Literal(54), Type.Literal(55),
Type.Literal(56), Type.Literal(57), Type.Literal(58), Type.Literal(59), Type.Literal(60), Type.Literal(61), Type.Literal(62), Type.Literal(63), // <- x64
Type.Literal(64), Type.Literal(65), Type.Literal(66), Type.Literal(67), Type.Literal(68), Type.Literal(69), Type.Literal(70), Type.Literal(71),
Type.Literal(72), Type.Literal(73), Type.Literal(74), Type.Literal(75), Type.Literal(76), Type.Literal(77), Type.Literal(78), Type.Literal(79),
Type.Literal(80), Type.Literal(81), Type.Literal(82), Type.Literal(83), Type.Literal(84), Type.Literal(85), Type.Literal(86), Type.Literal(87),
Type.Literal(88), Type.Literal(89), Type.Literal(90), Type.Literal(91), Type.Literal(92), Type.Literal(93), Type.Literal(94), Type.Literal(95),
Type.Literal(96), Type.Literal(97), Type.Literal(98), Type.Literal(99), Type.Literal(100), Type.Literal(101), Type.Literal(102), Type.Literal(103),
Type.Literal(104), Type.Literal(105), Type.Literal(106), Type.Literal(107), Type.Literal(108), Type.Literal(109), Type.Literal(110), Type.Literal(111),
Type.Literal(112), Type.Literal(113), Type.Literal(114), Type.Literal(115), Type.Literal(116), Type.Literal(117), Type.Literal(118), Type.Literal(119),
Type.Literal(120), Type.Literal(121), Type.Literal(122), Type.Literal(123), Type.Literal(124), Type.Literal(125), Type.Literal(126), Type.Literal(127), // <- x128
]), Type.String())
const B = Type.Record(Type.Union([
Type.Union([
Type.Literal(0), Type.Literal(1), Type.Literal(2), Type.Literal(3), Type.Literal(4), Type.Literal(5), Type.Literal(6), Type.Literal(7),
Type.Literal(8), Type.Literal(9), Type.Literal(10), Type.Literal(11), Type.Literal(12), Type.Literal(13), Type.Literal(14), Type.Literal(15),
Type.Literal(16), Type.Literal(17), Type.Literal(18), Type.Literal(19), Type.Literal(20), Type.Literal(21), Type.Literal(22), Type.Literal(23),
Type.Literal(24), Type.Literal(25), Type.Literal(26), Type.Literal(27), Type.Literal(28), Type.Literal(29), Type.Literal(30), Type.Literal(31),
]),
Type.Union([
Type.Literal(32), Type.Literal(33), Type.Literal(34), Type.Literal(35), Type.Literal(36), Type.Literal(37), Type.Literal(38), Type.Literal(39),
Type.Literal(40), Type.Literal(41), Type.Literal(42), Type.Literal(43), Type.Literal(44), Type.Literal(45), Type.Literal(46), Type.Literal(47),
Type.Literal(48), Type.Literal(49), Type.Literal(50), Type.Literal(51), Type.Literal(52), Type.Literal(53), Type.Literal(54), Type.Literal(55),
Type.Literal(56), Type.Literal(57), Type.Literal(58), Type.Literal(59), Type.Literal(60), Type.Literal(61), Type.Literal(62), Type.Literal(63), // <- x64
]),
Type.Union([
Type.Literal(64), Type.Literal(65), Type.Literal(66), Type.Literal(67), Type.Literal(68), Type.Literal(69), Type.Literal(70), Type.Literal(71),
Type.Literal(72), Type.Literal(73), Type.Literal(74), Type.Literal(75), Type.Literal(76), Type.Literal(77), Type.Literal(78), Type.Literal(79),
Type.Literal(80), Type.Literal(81), Type.Literal(82), Type.Literal(83), Type.Literal(84), Type.Literal(85), Type.Literal(86), Type.Literal(87),
Type.Literal(88), Type.Literal(89), Type.Literal(90), Type.Literal(91), Type.Literal(92), Type.Literal(93), Type.Literal(94), Type.Literal(95),
]),
Type.Union([
Type.Literal(96), Type.Literal(97), Type.Literal(98), Type.Literal(99), Type.Literal(100), Type.Literal(101), Type.Literal(102), Type.Literal(103),
Type.Literal(104), Type.Literal(105), Type.Literal(106), Type.Literal(107), Type.Literal(108), Type.Literal(109), Type.Literal(110), Type.Literal(111),
Type.Literal(112), Type.Literal(113), Type.Literal(114), Type.Literal(115), Type.Literal(116), Type.Literal(117), Type.Literal(118), Type.Literal(119),
Type.Literal(120), Type.Literal(121), Type.Literal(122), Type.Literal(123), Type.Literal(124), Type.Literal(125), Type.Literal(126), Type.Literal(127), // <- x128
])
]), Type.String())
type A = Static<typeof A>
Expect(B).ToStatic<A>()
}

100
test/static/recursive.ts Normal file
View File

@@ -0,0 +1,100 @@
import { Static, Type } from '@sinclair/typebox'
import { Expect } from './assert'
{
// identity
const R = Type.Recursive((Node) =>
Type.Object({
id: Type.String(),
nodes: Type.Array(Node),
}),
)
type T = Static<typeof R>
Expect(R).ToStatic<{ id: string; nodes: T[] }>()
}
{
// keyof
const R = Type.Recursive((Node) =>
Type.Object({
id: Type.String(),
nodes: Type.Array(Node),
}),
)
const T = Type.KeyOf(R)
Expect(T).ToStatic<'id' | 'nodes'>()
}
{
// partial
const R = Type.Recursive((Node) =>
Type.Object({
id: Type.String(),
nodes: Type.Array(Node),
}),
)
const T = Type.Partial(R)
Expect(T).ToStatic<{
id?: string | undefined
nodes?: Static<typeof T>[] | undefined
}>()
}
{
// required
const R = Type.Recursive((Node) =>
Type.Object({
id: Type.String(),
nodes: Type.Array(Node),
}),
)
const P = Type.Partial(R)
const T = Type.Required(P)
Expect(T).ToStatic<{
id: string
nodes: Static<typeof T>[]
}>()
}
{
// pick
const R = Type.Recursive((Node) =>
Type.Object({
id: Type.String(),
nodes: Type.Array(Node),
}),
)
const T = Type.Pick(R, ['id'])
Expect(T).ToStatic<{
id: string
}>()
}
{
// omit
const R = Type.Recursive((Node) =>
Type.Object({
id: Type.String(),
nodes: Type.Array(Node),
}),
)
const T = Type.Omit(R, ['id'])
Expect(T).ToStatic<{
nodes: Static<typeof T>[]
}>()
}
// prettier-ignore
{
// issue: https://github.com/sinclairzx81/typebox/issues/336
type JSONValue =
| string
| number
| null
| boolean
| { [x: string]: JSONValue }
| JSONValue[]
const R = Type.Recursive((Node) => Type.Union([
Type.Null(),
Type.String(),
Type.Number(),
Type.Boolean(),
Type.Record(Type.String(), Node),
Type.Array(Node)
]))
Expect(R).ToStatic<JSONValue>()
}

13
test/static/ref.ts Normal file
View File

@@ -0,0 +1,13 @@
import { Expect } from './assert'
import { Type, Static } from '@sinclair/typebox'
{
const T = Type.String({ $id: 'T' })
const R = Type.Ref('T')
type T = Static<typeof T>
type R = Static<typeof R>
Expect(T).ToStatic<string>()
Expect(R).ToStatic<unknown>()
}

4
test/static/regexp.ts Normal file
View File

@@ -0,0 +1,4 @@
import { Expect } from './assert'
import { Type } from '@sinclair/typebox'
Expect(Type.RegExp(/foo/)).ToStatic<string>()

107
test/static/required.ts Normal file
View File

@@ -0,0 +1,107 @@
import { Expect } from './assert'
import { Type, Static } from '@sinclair/typebox'
import * as Types from '@sinclair/typebox'
{
const T = Type.Required(
Type.Object({
A: Type.Optional(Type.String()),
B: Type.Optional(Type.String()),
C: Type.Optional(Type.String()),
}),
)
type T = Static<typeof T>
Expect(T).ToStatic<{
A: string
B: string
C: string
}>()
}
{
{
const A = Type.Object({ type: Type.Literal('A') })
const B = Type.Object({ type: Type.Literal('B') })
const C = Type.Object({ type: Type.Literal('C') })
const Union = Type.Union([A, B, C])
const Extended = Type.Object({
x: Type.Number(),
y: Type.Number(),
z: Type.Number(),
})
const T = Type.Intersect([Union, Extended])
const P = Type.Partial(T)
Expect(P).ToStatic<
(
| {
type?: 'A' | undefined
}
| {
type?: 'B' | undefined
}
| {
type?: 'C' | undefined
}
) & {
x?: number | undefined
y?: number | undefined
z?: number | undefined
}
>()
const R = Type.Required(P)
Expect(R).ToStatic<
(
| {
type: 'A'
}
| {
type: 'B'
}
| {
type: 'C'
}
) & {
x: number
y: number
z: number
}
>()
}
}
{
// https://github.com/sinclairzx81/typebox/issues/655
const T = Type.Object({
a: Type.ReadonlyOptional(Type.Number()),
b: Type.Readonly(Type.Number()),
c: Type.Optional(Type.Number()),
d: Type.Number(),
})
const R: Types.TObject<{
a: Types.TReadonly<Types.TNumber>
b: Types.TReadonly<Types.TNumber>
c: Types.TNumber
d: Types.TNumber
}> = Type.Required(T)
}
// ------------------------------------------------------------------
// Intrinsic Passthough
// https://github.com/sinclairzx81/typebox/issues/1169
// ------------------------------------------------------------------
// prettier-ignore
{
const T = Type.Required(Type.Union([Type.Number(), Type.Object({
x: Type.Optional(Type.Number())
})]))
Expect(T).ToStatic<number | { x: number }>
}
// prettier-ignore
{
const T = Type.Required(Type.Union([Type.Literal(1), Type.Object({
x: Type.Optional(Type.Number())
})]))
Expect(T).ToStatic<1 | { x: number }>
}

52
test/static/rest.ts Normal file
View File

@@ -0,0 +1,52 @@
import { Expect } from './assert'
import { Type, Static } from '@sinclair/typebox'
{
// union never
const A = Type.String()
const B = Type.Union(Type.Rest(A))
Expect(B).ToStaticNever()
}
{
// intersect never
const A = Type.String()
const B = Type.Intersect(Type.Rest(A))
Expect(B).ToStaticNever()
}
{
// tuple
const A = Type.Tuple([Type.Number(), Type.String()])
const B = Type.Union(Type.Rest(A))
Expect(B).ToStatic<string | number>()
}
{
// tuple spread
const A = Type.Tuple([Type.Literal(1), Type.Literal(2)])
const B = Type.Tuple([Type.Literal(3), Type.Literal(4)])
const C = Type.Tuple([...Type.Rest(A), ...Type.Rest(B)])
Expect(C).ToStatic<[1, 2, 3, 4]>()
}
{
// union to intersect
const A = Type.Object({ x: Type.Number() })
const B = Type.Object({ y: Type.String() })
const C = Type.Union([A, B])
const D = Type.Intersect(Type.Rest(C))
Expect(D).ToStatic<
{
x: number
} & {
y: string
}
>()
}
{
// intersect to composite
const A = Type.Object({ x: Type.Number() })
const B = Type.Object({ y: Type.String() })
const C = Type.Intersect([A, B])
const D = Type.Composite(Type.Rest(C))
Expect(D).ToStatic<{
x: number
y: string
}>()
}

View File

@@ -0,0 +1,18 @@
import { Expect } from './assert'
import { Type, Static } from '@sinclair/typebox'
{
const T = Type.ReturnType(Type.Function([], Type.String()))
type T = Static<typeof T>
Expect(T).ToStatic<string>()
}
{
const T = Type.ReturnType(Type.Function([Type.Number()], Type.Number()))
type T = Static<typeof T>
Expect(T).ToStatic<number>()
}

4
test/static/string.ts Normal file
View File

@@ -0,0 +1,4 @@
import { Expect } from './assert'
import { Type } from '@sinclair/typebox'
Expect(Type.String()).ToStatic<string>()

4
test/static/symbol.ts Normal file
View File

@@ -0,0 +1,4 @@
import { Expect } from './assert'
import { Type } from '@sinclair/typebox'
Expect(Type.Symbol()).ToStatic<symbol>()

35
test/static/syntax.ts Normal file
View File

@@ -0,0 +1,35 @@
import { Expect } from './assert'
import { Syntax } from '@sinclair/typebox/syntax'
// prettier-ignore
{
const Basis = Syntax(`{
x: 1,
y: 2,
z: 3
}`)
Expect(Basis).ToStatic<{
x: 1,
y: 2,
z: 3,
}>()
}
// prettier-ignore
{
const Vector = Syntax(`<X, Y, Z>{
x: X,
y: Y,
z: Z
}`)
const Basis = Syntax({ Vector }, `{
x: Vector<1, 0, 0>,
y: Vector<0, 1, 0>,
z: Vector<0, 0, 1>,
}`)
Expect(Basis).ToStatic<{
x: { x: 1, y: 0, z: 0 },
y: { x: 0, y: 1, z: 0 },
z: { x: 0, y: 0, z: 1 },
}>()
}

View File

@@ -0,0 +1,129 @@
import { Expect } from './assert'
import { Type } from '@sinclair/typebox'
{
// Empty
const T = Type.TemplateLiteral([])
Expect(T).ToStatic<''>()
}
{
// Literal
const T = Type.TemplateLiteral([Type.Literal('hello')])
Expect(T).ToStatic<'hello'>()
}
{
// And Sequence
const T = Type.TemplateLiteral([Type.Literal('hello'), Type.Literal('world')])
Expect(T).ToStatic<'helloworld'>()
}
{
// And / Or Sequence
const T = Type.TemplateLiteral([Type.Literal('hello'), Type.Union([Type.Literal('1'), Type.Literal('2')])])
Expect(T).ToStatic<'hello1' | 'hello2'>()
}
{
// Auxiliary Template
const A = Type.TemplateLiteral([Type.Union([Type.Literal('1'), Type.Literal('2')])])
const T = Type.TemplateLiteral([Type.Literal('hello'), A])
Expect(T).ToStatic<'hello1' | 'hello2'>()
}
{
// TemplateLiteral Composition
const A = Type.TemplateLiteral('${A|B}')
const B = Type.TemplateLiteral('${C|D}')
const T = Type.TemplateLiteral([A, B])
Expect(T).ToStatic<'AC' | 'AD' | 'BC' | 'BD'>()
}
{
// String
const T = Type.TemplateLiteral([Type.String()])
Expect(T).ToStatic<`${string}`>()
}
{
// Number
const T = Type.TemplateLiteral([Type.Number()])
Expect(T).ToStatic<`${number}`>()
}
{
// Boolean
const T = Type.TemplateLiteral([Type.Boolean()])
Expect(T).ToStatic<`${boolean}`>()
}
{
// Enum Implicit
enum E {
A,
B,
C,
}
const A = Type.Enum(E)
const T = Type.TemplateLiteral([Type.Literal('hello'), A])
Expect(T).ToStatic<'hello0' | 'hello1' | 'hello2'>()
}
{
// Enum Explicit
enum E {
A,
B = 'B',
C = 'C',
}
const A = Type.Enum(E)
const T = Type.TemplateLiteral([Type.Literal('hello'), A])
Expect(T).ToStatic<'hello0' | 'helloB' | 'helloC'>()
}
{
// Enum Object Explicit
const A = Type.Enum(Object.freeze({ a: 'A', b: 'B' }))
const T = Type.TemplateLiteral([Type.Literal('hello'), A])
Expect(T).ToStatic<'helloA' | 'helloB'>()
}
// ------------------------------------------------------------------
// Dollar Sign Escape
// https://github.com/sinclairzx81/typebox/issues/794
// ------------------------------------------------------------------
// prettier-ignore
{
const T = Type.TemplateLiteral('$prop${A|B|C}') // issue
Expect(T).ToStatic<'$propA' | '$propB' | '$propC'>()
}
// prettier-ignore
{
const T = Type.TemplateLiteral('$prop${A|B|C}x') // trailing
Expect(T).ToStatic<'$propAx' | '$propBx' | '$propCx'>()
}
// prettier-ignore
{
const T = Type.TemplateLiteral('$prop${A|B|C}x}') // non-greedy
Expect(T).ToStatic<'$propAx}' | '$propBx}' | '$propCx}'>()
}
// prettier-ignore
{
const T = Type.TemplateLiteral('$prop${A|B|C}x}${X|Y}') // distributive - non-greedy
Expect(T).ToStatic<
'$propAx}X' | '$propBx}X' | '$propCx}X' |
'$propAx}Y' | '$propBx}Y' | '$propCx}Y'
>()
}
// prettier-ignore
{
const T = Type.TemplateLiteral('$prop${A|B|C}x}${X|Y}x') // distributive - non-greedy - trailing
Expect(T).ToStatic<
'$propAx}Xx' | '$propBx}Xx' | '$propCx}Xx' |
'$propAx}Yx' | '$propBx}Yx' | '$propCx}Yx'
>()
}
// ---------------------------------------------------------------------
// issue: https://github.com/sinclairzx81/typebox/issues/913
// ---------------------------------------------------------------------
{
const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B')])])
const B = Type.TemplateLiteral([Type.Union([Type.Literal('X'), Type.Literal('Y')])])
const L = Type.TemplateLiteral([Type.Literal('KEY'), A, B])
const T = Type.Mapped(L, (K) => Type.Null())
Expect(T).ToStatic<{
KEYAX: null
KEYAY: null
KEYBX: null
KEYBY: null
}>()
}

376
test/static/transform.ts Normal file
View File

@@ -0,0 +1,376 @@
import { Type, TSchema, Static, StaticDecode, TObject, TNumber } from '@sinclair/typebox'
import { TypeCheck } from '@sinclair/typebox/compiler'
import { Value } from '@sinclair/typebox/value'
import { Expect } from './assert'
{
// string > number
const T = Type.Transform(Type.Number())
.Decode((value) => value.toString())
.Encode((value) => parseFloat(value))
Expect(T).ToStaticDecode<string>()
Expect(T).ToStaticEncode<number>()
Expect(T).ToStatic<number>()
}
{
// boolean > union
const T = Type.Transform(Type.Boolean())
.Decode((value) => (value ? (1 as const) : (2 as const)))
.Encode((value) => true)
Expect(T).ToStatic<boolean>()
Expect(T).ToStaticDecode<1 | 2>()
Expect(T).ToStaticEncode<boolean>()
}
{
// literal > union
const T = Type.Transform(Type.Union([Type.Literal(1), Type.Literal(2)]))
.Decode((value) => true as const)
.Encode((value) => (value ? (1 as const) : (2 as const)))
Expect(T).ToStatic<1 | 2>()
Expect(T).ToStaticDecode<true>()
Expect(T).ToStaticEncode<1 | 2>()
}
{
// nested: 1 > 2 > 3
const T1 = Type.Transform(Type.Literal(1))
.Decode((value) => 2 as const)
.Encode((value) => 1 as const)
const T2 = Type.Transform(T1)
.Decode((value) => 3 as const)
.Encode((value) => 2 as const)
Expect(T1).ToStatic<1>()
Expect(T1).ToStaticDecode<2>()
Expect(T1).ToStaticEncode<1>()
Expect(T2).ToStatic<2>() // resolve to base
Expect(T2).ToStaticDecode<3>()
Expect(T2).ToStaticEncode<2>()
}
{
// nested: 1 > 2 > 3 > 4
const T1 = Type.Transform(Type.Literal(1))
.Decode((value) => 2 as const)
.Encode((value) => 1 as const)
const T2 = Type.Transform(T1)
.Decode((value) => 3 as const)
.Encode((value) => 2 as const)
const T3 = Type.Transform(T2)
.Decode((value) => 4 as const)
.Encode((value) => 3 as const)
Expect(T1).ToStatic<1>()
Expect(T1).ToStaticDecode<2>()
Expect(T1).ToStaticEncode<1>()
Expect(T2).ToStatic<2>()
Expect(T2).ToStaticDecode<3>()
Expect(T2).ToStaticEncode<2>()
Expect(T3).ToStatic<3>()
Expect(T3).ToStaticDecode<4>()
Expect(T3).ToStaticEncode<3>()
}
{
// recursive > 1
// prettier-ignore
const T = Type.Transform(Type.Recursive(This => Type.Object({
id: Type.String(),
nodes: Type.Array(This)
})))
.Decode((value) => 1 as const)
.Encode((value) => ({
id: 'A',
nodes: [
{ id: 'B', nodes: [] },
{ id: 'C', nodes: [] }
]
}))
interface N {
id: string
nodes: this[]
}
Expect(T).ToStatic<N>()
Expect(T).ToStaticDecode<1>()
Expect(T).ToStaticEncode<N>()
}
{
// recursive > 1 > 2
interface N {
id: string
nodes: this[]
}
// prettier-ignore
const T1 = Type.Transform(Type.Recursive(This => Type.Object({
id: Type.String(),
nodes: Type.Array(This)
})))
.Decode((value) => 1 as const)
.Encode((value) => ({
id: 'A',
nodes: [
{ id: 'B', nodes: [] },
{ id: 'C', nodes: [] }
]
}))
const T2 = Type.Transform(T1)
.Decode((value) => 2 as const)
.Encode((value) => 1 as const)
Expect(T1).ToStatic<N>()
Expect(T1).ToStaticDecode<1>()
Expect(T1).ToStaticEncode<N>()
Expect(T2).ToStatic<1>()
Expect(T2).ToStaticDecode<2>()
Expect(T2).ToStaticEncode<1>()
}
{
// deep-nesting
// prettier-ignore
const T = Type.Transform(Type.Transform(Type.Transform(Type.Transform(Type.Transform(Type.Transform(Type.Transform(Type.Transform(Type.Transform(Type.Literal(1))
.Decode((value) => value)
.Encode((value) => value))
.Decode((value) => value)
.Encode((value) => value))
.Decode((value) => value)
.Encode((value) => value))
.Decode((value) => value)
.Encode((value) => value))
.Decode((value) => value)
.Encode((value) => value))
.Decode((value) => value)
.Encode((value) => value))
.Decode((value) => value)
.Encode((value) => value))
.Decode((value) => value)
.Encode((value) => value))
.Decode((value) => value)
.Encode((value) => value)
Expect(T).ToStatic<1>()
Expect(T).ToStaticDecode<1>()
Expect(T).ToStaticEncode<1>()
}
{
// null to typebox type
// prettier-ignore
const T = Type.Transform(Type.Null())
.Decode(value => Type.Object({
x: Type.Number(),
y: Type.Number(),
z: Type.Number()
}))
.Encode(value => null)
Expect(T).ToStatic<null>()
Expect(T).ToStaticDecode<
TObject<{
x: TNumber
y: TNumber
z: TNumber
}>
>()
Expect(T).ToStaticEncode<null>()
type T = StaticDecode<typeof T>
type S = Static<T> // type S = {
// x: number;
// y: number;
// z: number;
// }
}
{
// ensure decode as optional
// prettier-ignore
const T = Type.Object({
x: Type.Optional(Type.Number()),
y: Type.Optional(Type.Number())
})
Expect(T).ToStaticDecode<{ x?: number | undefined; y?: number | undefined }>()
}
{
// ensure decode as readonly
// prettier-ignore
const T = Type.Object({
x: Type.Readonly(Type.Number()),
y: Type.Readonly(Type.Number())
})
Expect(T).ToStaticDecode<{ readonly x: number; readonly y: number }>()
}
{
// ensure decode as optional union
// prettier-ignore
const T = Type.Object({
x: Type.Optional(Type.Union([
Type.String(),
Type.Number()
]))
})
Expect(T).ToStaticDecode<{ x?: string | number | undefined }>()
}
{
// should decode within generic function context
// https://github.com/sinclairzx81/typebox/issues/554
// prettier-ignore
// const ArrayOrSingle = <T extends TSchema>(schema: T) =>
// Type.Transform(Type.Union([schema, Type.Array(schema)])[0])
// .Decode((value) => (Array.isArray(value) ? value : [value]))
// .Encode((value) => (value.length === 1 ? value[0] : value) as Static<T>[]);
// const T = ArrayOrSingle(Type.String())
// Expect(T).ToStaticDecode<string[]>()
}
{
// 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<typeof T>
type Test<A, E> = E extends A ? true : false
type E1 = Test<A, {}>
type E2 = Test<A, { x: undefined }>
type E3 = Test<A, { x: { 1: '' } }>
type E4 = Test<A, { x: { 1: 1 } }>
type E5 = Test<A, { x: { x: 1 } }>
// assignment
const E1: E1 = true
const E2: E2 = true
const E3: E3 = true
const E4: E4 = false
const E5: E5 = true
}
{
// should correctly decode array
// https://github.com/sinclairzx81/typebox/issues/561
const T = Type.Object({
x: Type.Array(Type.Object({ y: Type.String() })),
})
Expect(T).ToStaticDecode<{ x: { y: string }[] }>()
}
{
// should decode generic union
const GenericUnion = <T extends TSchema>(t: T) => Type.Union([t, Type.Null()])
const T = Type.Transform(Type.String())
.Decode((value) => new Date(value))
.Encode((value) => value.toISOString())
Expect(T).ToStaticDecode<Date>()
Expect(GenericUnion(T)).ToStaticDecode<Date | null>()
}
{
// should decode generic tuple
const GenericTuple = <T extends TSchema>(t: T) => Type.Tuple([t, Type.Null()])
const T = Type.Transform(Type.String())
.Decode((value) => new Date(value))
.Encode((value) => value.toISOString())
Expect(T).ToStaticDecode<Date>()
Expect(GenericTuple(T)).ToStaticDecode<[Date, null]>()
}
{
// should decode generic intersect
const GenericIntersect = <T extends TSchema>(t: T) => Type.Intersect([t, Type.Literal(1)])
const T = Type.Transform(Type.Number())
.Decode((value) => value)
.Encode((value) => value)
Expect(T).ToStaticDecode<number>()
Expect(GenericIntersect(T)).ToStaticDecode<1>()
}
{
// should decode enum
enum E {
A,
B,
C,
}
const T = Type.Transform(Type.Enum(E))
.Decode((value) => 1 as const)
.Encode((value) => E.A)
Expect(T).ToStaticDecode<1>()
Expect(T).ToStaticEncode<E>()
}
{
// 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<new (x: Date) => Date>()
Expect(T).ToStaticEncode<new (x: number) => number>()
}
// -------------------------------------------------------------
// https://github.com/sinclairzx81/typebox/issues/798
// -------------------------------------------------------------
{
const c1: TypeCheck<any> = {} as any
const x1 = c1.Decode({})
const x2 = Value.Decode({} as any, {})
}
// -------------------------------------------------------------
// https://github.com/sinclairzx81/typebox/issues/1178
// -------------------------------------------------------------
// immediate
{
const T = Type.Module({
A: Type.Transform(Type.String())
.Decode((value) => parseInt(value))
.Encode((value) => value.toString()),
}).Import('A')
Expect(T).ToStaticDecode<number>()
Expect(T).ToStaticEncode<string>()
}
// referential
{
const T = Type.Module({
A: Type.Transform(Type.String())
.Decode((value) => parseInt(value))
.Encode((value) => value.toString()),
B: Type.Ref('A'),
}).Import('B')
Expect(T).ToStaticDecode<number>()
Expect(T).ToStaticEncode<string>()
}
// deep-referential
{
const T = Type.Module({
A: Type.Transform(Type.String())
.Decode((value) => parseInt(value))
.Encode((value) => value.toString()),
B: Type.Ref('A'),
C: Type.Ref('B'),
D: Type.Ref('C'),
E: Type.Ref('D'),
}).Import('E')
Expect(T).ToStaticDecode<number>()
Expect(T).ToStaticEncode<string>()
}
// interior-transform referential
{
const T = Type.Module({
A: Type.String(),
B: Type.Ref('A'),
C: Type.Ref('B'),
T: Type.Transform(Type.Ref('C'))
.Decode((value) => parseInt(value as string))
.Encode((value) => value.toString()),
X: Type.Ref('T'),
Y: Type.Ref('X'),
Z: Type.Ref('Y'),
}).Import('Z')
Expect(T).ToStaticDecode<number>()
Expect(T).ToStaticEncode<string>()
}

View File

@@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.json",
"files": ["index.ts"]
}

10
test/static/tuple.ts Normal file
View File

@@ -0,0 +1,10 @@
import { Expect } from './assert'
import { Type, Static } from '@sinclair/typebox'
{
const T = Type.Tuple([Type.Number(), Type.String(), Type.Boolean()])
type T = Static<typeof T>
Expect(T).ToStatic<[number, string, boolean]>()
}

View File

@@ -0,0 +1,14 @@
import { Expect } from './assert'
import { Type } from '@sinclair/typebox'
Expect(Type.Uncapitalize(Type.Literal('HELLO'))).ToStatic<'hELLO'>()
Expect(Type.Uncapitalize(Type.Union([Type.Literal('HELLO'), Type.Literal('WORLD')]))).ToStatic<'hELLO' | 'wORLD'>()
Expect(Type.Uncapitalize(Type.TemplateLiteral('HELLO${0|1}'))).ToStatic<'hELLO0' | 'hELLO1'>()
// prettier-ignore
Expect(Type.Uncapitalize(Type.TemplateLiteral([Type.Literal('HELLO'), Type.Union([Type.Literal(1), Type.Literal(2)])]))).ToStatic<'hELLO1' | 'hELLO2'>()
// passthrough
Expect(Type.Uncapitalize(Type.Object({ x: Type.Number() }))).ToStatic<{ x: number }>()

106
test/static/union.ts Normal file
View File

@@ -0,0 +1,106 @@
import { Expect } from './assert'
import { Type, Static } from '@sinclair/typebox'
{
const A = Type.String()
const B = Type.Number()
const T = Type.Union([A, B])
type T = Static<typeof T>
Expect(T).ToStatic<string | number>()
}
{
const A = Type.Object({
A: Type.String(),
B: Type.String(),
})
const B = Type.Object({
X: Type.Number(),
Y: Type.Number(),
})
const T = Type.Union([A, B])
type T = Static<typeof T>
Expect(T).ToStatic<
| {
A: string
B: string
}
| {
X: number
Y: number
}
>()
}
{
const A = Type.Object({
A: Type.String(),
B: Type.String(),
})
const B = Type.Object({
X: Type.Number(),
Y: Type.Number(),
})
const T = Type.Union([A, B, Type.Intersect([A, B])])
type T = Static<typeof T>
Expect(T).ToStatic<
| {
A: string
B: string
}
| {
X: number
Y: number
}
| ({
A: string
B: string
} & {
X: number
Y: number
})
>()
}
{
const T = Type.Union([])
Expect(T).ToStaticNever()
}
// prettier-ignore
{ // Scalable Union
const X = Type.Object({ x: Type.Number() })
const Y = Type.Object({ y: Type.Number() })
const Z = Type.Object({ z: Type.Number() })
const W = Type.Object({ w: Type.Number() })
const T = Type.Union([
X, Y, Z, W, X, Y, Z, W,
X, Y, Z, W, X, Y, Z, W,
X, Y, Z, W, X, Y, Z, W,
X, Y, Z, W, X, Y, Z, W,
X, Y, Z, W, X, Y, Z, W,
X, Y, Z, W, X, Y, Z, W,
X, Y, Z, W, X, Y, Z, W,
X, Y, Z, W, X, Y, Z, W,
X, Y, Z, W, X, Y, Z, W,
X, Y, Z, W, X, Y, Z, W,
X, Y, Z, W, X, Y, Z, W,
X, Y, Z, W, X, Y, Z, W,
X, Y, Z, W, X, Y, Z, W,
X, Y, Z, W, X, Y, Z, W,
X, Y, Z, W, X, Y, Z, W,
X, Y, Z, W, X, Y, Z, W,
])
Expect(T).ToStatic<
{ x: number } |
{ y: number } |
{ z: number } |
{ w: number }
>()
}

4
test/static/unknown.ts Normal file
View File

@@ -0,0 +1,4 @@
import { Expect } from './assert'
import { Type } from '@sinclair/typebox'
Expect(Type.Unknown()).ToStatic<unknown>()

14
test/static/uppercase.ts Normal file
View File

@@ -0,0 +1,14 @@
import { Expect } from './assert'
import { Type } from '@sinclair/typebox'
Expect(Type.Uppercase(Type.Literal('hello'))).ToStatic<'HELLO'>()
Expect(Type.Uppercase(Type.Union([Type.Literal('hello'), Type.Literal('world')]))).ToStatic<'HELLO' | 'WORLD'>()
Expect(Type.Uppercase(Type.TemplateLiteral('HELLO${0|1}'))).ToStatic<'HELLO0' | 'HELLO1'>()
// prettier-ignore
Expect(Type.Uppercase(Type.TemplateLiteral([Type.Literal('hello'), Type.Union([Type.Literal(1), Type.Literal(2)])]))).ToStatic<'HELLO1' | 'HELLO2'>()
// passthrough
Expect(Type.Uppercase(Type.Object({ x: Type.Number() }))).ToStatic<{ x: number }>()