Publish
This commit is contained in:
4
test/static/any.ts
Normal file
4
test/static/any.ts
Normal 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
21
test/static/argument.ts
Normal 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
24
test/static/array.ts
Normal 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
70
test/static/assert.ts
Normal 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>
|
||||
}
|
||||
4
test/static/async-iterator.ts
Normal file
4
test/static/async-iterator.ts
Normal 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
20
test/static/awaited.ts
Normal 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
4
test/static/bigint.ts
Normal 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
4
test/static/boolean.ts
Normal 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
14
test/static/capitalize.ts
Normal 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
213
test/static/composite.ts
Normal 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
41
test/static/const.ts
Normal 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]>()
|
||||
13
test/static/constructor-parameters.ts
Normal file
13
test/static/constructor-parameters.ts
Normal 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]>()
|
||||
51
test/static/constructor.ts
Normal file
51
test/static/constructor.ts
Normal 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
4
test/static/date.ts
Normal 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
42
test/static/enum.ts
Normal 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
99
test/static/exclude.ts
Normal 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
100
test/static/extract.ts
Normal 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
54
test/static/function.ts
Normal 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
155
test/static/import.ts
Normal 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
58
test/static/index.ts
Normal 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
276
test/static/indexed.ts
Normal 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
53
test/static/intersect.ts
Normal 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
4
test/static/iterator.ts
Normal 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
102
test/static/keyof.ts
Normal 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
8
test/static/literal.ts
Normal 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
14
test/static/lowercase.ts
Normal 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
417
test/static/mapped.ts
Normal 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
18
test/static/modifier.ts
Normal 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
7
test/static/never.ts
Normal 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
26
test/static/not.ts
Normal 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
4
test/static/null.ts
Normal 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
4
test/static/number.ts
Normal 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
70
test/static/object.ts
Normal 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
104
test/static/omit.ts
Normal 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
49
test/static/optional.ts
Normal 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
13
test/static/parameters.ts
Normal 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
105
test/static/partial.ts
Normal 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
129
test/static/pick.ts
Normal 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
|
||||
}
|
||||
>()
|
||||
}
|
||||
28
test/static/readonly-optional.ts
Normal file
28
test/static/readonly-optional.ts
Normal 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
50
test/static/readonly.ts
Normal 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
257
test/static/record.ts
Normal 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
100
test/static/recursive.ts
Normal 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
13
test/static/ref.ts
Normal 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
4
test/static/regexp.ts
Normal 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
107
test/static/required.ts
Normal 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
52
test/static/rest.ts
Normal 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
|
||||
}>()
|
||||
}
|
||||
18
test/static/return-type.ts
Normal file
18
test/static/return-type.ts
Normal 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
4
test/static/string.ts
Normal 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
4
test/static/symbol.ts
Normal 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
35
test/static/syntax.ts
Normal 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 },
|
||||
}>()
|
||||
}
|
||||
129
test/static/template-literal.ts
Normal file
129
test/static/template-literal.ts
Normal 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
376
test/static/transform.ts
Normal 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>()
|
||||
}
|
||||
4
test/static/tsconfig.json
Normal file
4
test/static/tsconfig.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"files": ["index.ts"]
|
||||
}
|
||||
10
test/static/tuple.ts
Normal file
10
test/static/tuple.ts
Normal 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]>()
|
||||
}
|
||||
14
test/static/uncapitalize.ts
Normal file
14
test/static/uncapitalize.ts
Normal 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
106
test/static/union.ts
Normal 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
4
test/static/unknown.ts
Normal 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
14
test/static/uppercase.ts
Normal 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 }>()
|
||||
Reference in New Issue
Block a user