TypeBox is designed to be a programmable TypeScript based DSL for the JSON Schema specification. It uses JSON Schema as an intermediate representation (IR) for TypeScript types, and evaluates the IR using the semantics of the TypeScript language.
Script has support for most TypeScript types and syntactic constructs. Each type will map into its corresponding TypeBox IR representation.
// ------------------------------------------------------------------
// Literal
// ------------------------------------------------------------------
const T0 = Type.Script('1') // TLiteral<1>
const T1 = Type.Script('true') // TLiteral<true>
const T2 = Type.Script('"hello"') // TLiteral<"hello">
const T3 = Type.Script('10000n') // TLiteral<10000n>
// ------------------------------------------------------------------
// TemplateLiteral
// ------------------------------------------------------------------
const T4 = Type.Script('`hello${1|2}`') // TTemplateLiteral<'^hello(1|2)$'>
// ------------------------------------------------------------------
// JsonSchema
// ------------------------------------------------------------------
const T5 = Type.Script('any') // TAny
const T6 = Type.Script('unknown') // TUnknown
const T7 = Type.Script('never') // TNever
const T8 = Type.Script('boolean') // TBoolean
const T9 = Type.Script('integer') // TInteger
const T10 = Type.Script('number') // TNumber
const T11 = Type.Script('string') // TString
const T12 = Type.Script('null') // TNull
// ------------------------------------------------------------------
// JsonSchema Extensions
// ------------------------------------------------------------------
const T13 = Type.Script('bigint') // TBigInt
const T14 = Type.Script('symbol') // TSymbol
const T15 = Type.Script('undefined') // TUndefined
const T16 = Type.Script('void') // TVoid
// ------------------------------------------------------------------
// Structural
// ------------------------------------------------------------------
const T17 = Type.Script('{ x: number }') // TObject<{ x: TNumber }>
const T18 = Type.Script('[1, 2]') // TTuple<[TLiteral<1>, TLiteral<2>]>
const T19 = Type.Script('number[]') // TArray<TNumber>
// ------------------------------------------------------------------
// Intersect
// ------------------------------------------------------------------
const T20 = Type.Script('{ x: 1 } & { y: 2 }') // TIntersect<[
// TObject<{
// x: TLiteral<1>
// }>,
// TObject<{
// y: TLiteral<2>
// }>,
// ]>
// ------------------------------------------------------------------
// Union
// ------------------------------------------------------------------
const T21 = Type.Script('{ x: 1 } | { y: 2 }') // TUnion<[
// TObject<{
// x: TLiteral<1>
// }>,
// TObject<{
// y: TLiteral<2>
// }>,
// ]>
// ------------------------------------------------------------------
// Function
// ------------------------------------------------------------------
const T22 = Type.Script(`(a: number, b: number) => number`)
// TFunction<[TNumber, TNumber], TNumber>
// ------------------------------------------------------------------
// Constructor
// ------------------------------------------------------------------
const T23 = Type.Script(`new (a: number, b: number) => {
foo: (x: string) => void
bar: (x: string) => void
baz: (x: string) => void
}`) // TConstructor<[TNumber, TNumber], TObject<{
// foo: TFunction<[TString], TVoid>
// bar: TFunction<[TString], TVoid>
// baz: TFunction<[TString], TVoid>
// }>>
Script provides support for most TypeScript type-level expressions.
// ------------------------------------------------------------------
// Conditional
// ------------------------------------------------------------------
const T0 = Type.Script(`1 extends 2 ? true : false`) // TFalse
const T1 = Type.Script('1 extends infer A ? [A]: never') // TTuple<[TLiteral<1>]>
// ------------------------------------------------------------------
// Mapped
// ------------------------------------------------------------------
const T2 = Type.Script(`{
[K in keyof 'x' | 'y' | 'z']: number
}`) // TObject<{
// x: TNumber,
// y: TNumber,
// z: TNumber
// }>
const T3 = Type.Script(`{
[K in keyof 'x' | 'y' | 'z' as Uppercase<K>]?: number
}`) // TObject<{
// X: TOptional<TNumber>,
// Y: TOptional<TNumber>,
// Z: TOptional<TNumber>
// }>
// ------------------------------------------------------------------
// KeyOf
// ------------------------------------------------------------------
const T4 = Type.Script(`keyof { x: number, y: number }`) // TUnion<[
// TLiteral<'x'>,
// TLiteral<'y'>
// ]>
// ------------------------------------------------------------------
// Indexed
// ------------------------------------------------------------------
const T5 = Type.Script(`[1, 2, 3][2]`) // TLiteral<3>
const T6 = Type.Script(`{ x: number }['x']`) // TNumber
// ------------------------------------------------------------------
// Generic + Invoke
// ------------------------------------------------------------------
const T7 = Type.Script(`<T> = [T, T, T]`) // TGeneric<...>
const T8 = Type.Script({ T7 }, `T7<1>`) // TTuple<[
// TLiteral<1>,
// TLiteral<1>,
// TLiteral<1>,
// ]>
// ------------------------------------------------------------------
// Dependent
// ------------------------------------------------------------------
const T9 = Type.Script(`if number then 1`) // TDependent<
// TNumber,
// TLiteral<1>,
// TUnknown
// >
const T10 = Type.Script(`if number then 1 else string`) // TDependent<
// TNumber,
// TLiteral<1>,
// TString
// >
const T11 = Type.Script(`{
x: number
y: number
} & if { x: 1 } then { y: 1 }`) // TIntersect<[
// TObject<{
// x: TNumber,
// y: TNumber,
// }>,
// TDependent<
// TObject<{
// x: TLiteral<1>,
// }>,
// TObject<{
// y: TLiteral<2>,
// }>,
// TUnknown
// >
// ]>
Script supports extended syntax to augment types with constraints and annotations.
// ------------------------------------------------------------------
// With Types
// ------------------------------------------------------------------
const T0 = Type.Script(`string with {
format: 'email'
description: 'An email address'
}`) // TWith<TString, {
// format: 'email',
// description: 'An email address',
// }>
const T1 = Type.Script(`{
x: number with { minimum: 0, maximum: 1 },
y: number with { minimum: 0, maximum: 1 },
z: number with { minimum: 0, maximum: 1 },
} with { additionalProperties: false }`) // TWith<TObject<{
// x: TWith<TNumber, {
// minimum: 0;
// maximum: 1;
// }>;
// y: TWith<TNumber, {
// minimum: 0;
// maximum: 1;
// }>;
// z: TWith<TNumber, {
// minimum: 0;
// maximum: 1;
// }>;
// }>, {
// additionalProperties: false;
// }>
Script includes support for most TypeScript utility types and intrinsics.
// ------------------------------------------------------------------
// Utility Types
// ------------------------------------------------------------------
const T0 = Type.Script(`Partial<{ x: number }>`)
const T1 = Type.Script(`Required<{ x: number }>`)
const T2 = Type.Script(`Readonly<{ x: number }>`)
const T3 = Type.Script(`Pick<{ x: number, y: number }, 'x'>`)
const T4 = Type.Script(`Omit<{ x: number, y: number }, 'x'>`)
const T5 = Type.Script(`NonNullable<string | null>`)
const T6 = Type.Script('Exclude<1 | 2 | 3, 1 | 2>')
const T7 = Type.Script('Extract<1 | 2 | 3, 1 | 2>')
const T8 = Type.Script('ConstructorParameters<new (x: number) => {}>')
const T9 = Type.Script('Parameters<(x: number) => number>')
const T10 = Type.Script('ReturnType<(x: number) => number>')
const T11 = Type.Script('InstanceType<(x: number) => number>')
// ------------------------------------------------------------------
// Intrinsic String Manipulation
// ------------------------------------------------------------------
const T12 = Type.Script('Uppercase<"hello">')
const T13 = Type.Script('Lowercase<"hello">')
const T14 = Type.Script('Capitalize<"hello">')
const T15 = Type.Script('Uncapitalize<"hello">')
// ------------------------------------------------------------------
// Extension Types
// ------------------------------------------------------------------
const T16 = Type.Script('Evaluate<{ x: number } & { y: number }>')
The Script function also accepts type and interface declarations. The return value will be an object containing all embedded declarations.
const { Expression, ConstDeclaration, BinaryExpression, ConstExpression } = Type.Script(`
type Expression =
| ConstDeclaration
| BinaryExpression
| ConstExpression
interface ConstDeclaration {
type: 'ConstDeclaration'
name: string
value: Expression
}
interface BinaryExpression {
type: 'BinaryExpression'
left: Expression
right: Expression
}
interface ConstExpression {
type: 'ConstExpression'
const: unknown
}
`)
The following constructs are currently unsupported.
Script does not support substring inference with TemplateLiteral types.
type Get<S extends string> = S extends `hello ${infer Name}` ? Name : never
// ^
// Substring infer not supported
Script does not support embedded function types with generic type parameters.
type Foo = { x: <T>(value: T) => string }
// ^
// Embedded generic function not supported