Install
Node
$ npm install @sinclair/typebox --save
Deno
import { Static, Type } from 'https://deno.land/x/typebox/src/typebox.ts'
Example
import { Static, Type } from '@sinclair/typebox'
const T = Type.String() // const T = { type: 'string' }
type T = Static<typeof T> // type T = string
Overview
TypeBox is a type builder library that creates in-memory JSON Schema objects that can be statically inferred as TypeScript types. The schemas produced by this library are designed to match the static type checking rules of the TypeScript compiler. TypeBox enables one to create unified types that can be statically checked by TypeScript and runtime asserted using standard JSON Schema validation.
TypeBox can be used as a simple tool to build up complex schemas or integrated into RPC or REST services to help validate JSON data received over the wire.
License MIT
Contents
- Install
- Overview
- Usage
- Types
- Modifiers
- Options
- Extended Types
- Reference Types
- Recursive Types
- Generic Types
- Unsafe Types
- Conditional Types
- Values
- Guards
- Strict
- Validation
- Compiler
- Benchmark
- Contribute
Usage
The following demonstrates TypeBox's general usage.
import { Static, Type } from '@sinclair/typebox'
//--------------------------------------------------------------------------------------------
//
// Let's say you have the following type ...
//
//--------------------------------------------------------------------------------------------
type T = {
id: string,
name: string,
timestamp: number
}
//--------------------------------------------------------------------------------------------
//
// ... you can express this type in the following way.
//
//--------------------------------------------------------------------------------------------
const T = Type.Object({ // const T = {
id: Type.String(), // type: 'object',
name: Type.String(), // properties: {
timestamp: Type.Integer() // id: {
}) // type: 'string'
// },
// name: {
// type: 'string'
// },
// timestamp: {
// type: 'integer'
// }
// },
// required: [
// 'id',
// 'name',
// 'timestamp'
// ]
// }
//--------------------------------------------------------------------------------------------
//
// ... then infer back to the original static type this way.
//
//--------------------------------------------------------------------------------------------
type T = Static<typeof T> // type T = {
// id: string,
// name: string,
// timestamp: number
// }
//--------------------------------------------------------------------------------------------
//
// ... then use the type both as JSON schema and as a TypeScript type.
//
//--------------------------------------------------------------------------------------------
function receive(value: T) { // ... as a Type
if(JSON.validate(T, value)) { // ... as a Schema
// ok...
}
}
Types
The following table outlines the TypeBox mappings between TypeScript and JSON schema.
┌────────────────────────────────┬─────────────────────────────┬────────────────────────────────┐
│ TypeBox │ TypeScript │ JSON Schema │
│ │ │ │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Any() │ type T = any │ const T = { } │
│ │ │ │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Unknown() │ type T = unknown │ const T = { } │
│ │ │ │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.String() │ type T = string │ const T = { │
│ │ │ type: 'string' │
│ │ │ } │
│ │ │ │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Number() │ type T = number │ const T = { │
│ │ │ type: 'number' │
│ │ │ } │
│ │ │ │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Integer() │ type T = number │ const T = { │
│ │ │ type: 'integer' │
│ │ │ } │
│ │ │ │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Boolean() │ type T = boolean │ const T = { │
│ │ │ type: 'boolean' │
│ │ │ } │
│ │ │ │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Null() │ type T = null │ const T = { │
│ │ │ type: 'null' │
│ │ │ } │
│ │ │ │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.RegEx(/foo/) │ type T = string │ const T = { │
│ │ │ type: 'string', │
│ │ │ pattern: 'foo' │
│ │ │ } │
│ │ │ │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Literal(42) │ type T = 42 │ const T = { │
│ │ │ const: 42, │
│ │ │ type: 'number' │
│ │ │ } │
│ │ │ │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Array( │ type T = number[] │ const T = { │
│ Type.Number() │ │ type: 'array', │
│ ) │ │ items: { │
│ │ │ type: 'number' │
│ │ │ } │
│ │ │ } │
│ │ │ │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Object({ │ type T = { │ const T = { │
│ x: Type.Number(), │ x: number, │ type: 'object', │
│ y: Type.Number() │ y: number │ properties: { │
│ }) │ } │ x: { │
│ │ │ type: 'number' │
│ │ │ }, │
│ │ │ y: { │
│ │ │ type: 'number' │
│ │ │ } │
│ │ │ }, │
│ │ │ required: ['x', 'y'] │
│ │ │ } │
│ │ │ │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Tuple([ │ type T = [number, number] │ const T = { │
│ Type.Number(), │ │ type: 'array', │
│ Type.Number() │ │ items: [{ │
│ ]) │ │ type: 'number' │
│ │ │ }, { │
│ │ │ type: 'number' │
│ │ │ }], │
│ │ │ additionalItems: false, │
│ │ │ minItems: 2, │
│ │ │ maxItems: 2 │
│ │ │ } │
│ │ │ │
│ │ │ │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ enum Foo { │ enum Foo { │ const T = { │
│ A, │ A, │ anyOf: [{ │
│ B │ B │ type: 'number', │
│ } │ } │ const: 0 │
│ │ │ }, { │
│ const T = Type.Enum(Foo) │ type T = Foo │ type: 'number', │
│ │ │ const: 1 │
│ │ │ }] │
│ │ │ } │
│ │ │ │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.KeyOf( │ type T = keyof { │ const T = { │
│ Type.Object({ │ x: number, │ anyOf: [{ │
│ x: Type.Number(), │ y: number │ type: 'string', │
│ y: Type.Number() │ } │ const: 'x' │
│ }) │ │ }, { │
│ ) │ │ type: 'string', │
│ │ │ const: 'y' │
│ │ │ }] │
│ │ │ } │
│ │ │ │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Union([ │ type T = string | number │ const T = { │
│ Type.String(), │ │ anyOf: [{ │
│ Type.Number() │ │ type: 'string' │
│ ]) │ │ }, { │
│ │ │ type: 'number' │
│ │ │ }] │
│ │ │ } │
│ │ │ │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Intersect([ │ type T = { │ const T = { │
│ Type.Object({ │ x: number │ type: 'object', │
│ x: Type.Number() │ } & { │ properties: { │
│ }), │ y: number │ x: { │
│ Type.Object({ │ } │ type: 'number' │
│ y: Type.Number() │ │ }, │
│ }) │ │ y: { │
│ ]) │ │ type: 'number' │
│ │ │ } │
│ │ │ }, │
│ │ │ required: ['x', 'y'] │
│ │ │ } │
│ │ │ │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Record( │ type T = Record< │ const T = { │
│ Type.String(), │ string, │ type: 'object', │
│ Type.Number() │ number, │ patternProperties: { │
│ ) │ > │ '^.*$': { │
│ │ │ type: 'number' │
│ │ │ } │
│ │ │ } │
│ │ │ } │
│ │ │ │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Partial( │ type T = Partial<{ │ const T = { │
│ Type.Object({ │ x: number, │ type: 'object', │
│ x: Type.Number(), │ y: number │ properties: { │
│ y: Type.Number() | }> │ x: { │
│ }) │ │ type: 'number' │
│ ) │ │ }, │
│ │ │ y: { │
│ │ │ type: 'number' │
│ │ │ } │
│ │ │ } │
│ │ │ } │
│ │ │ │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Required( │ type T = Required<{ │ const T = { │
│ Type.Object({ │ x?: number, │ type: 'object', │
│ x: Type.Optional( │ y?: number │ properties: { │
│ Type.Number() | }> │ x: { │
│ ), │ │ type: 'number' │
│ y: Type.Optional( │ │ }, │
│ Type.Number() │ │ y: { │
│ ) │ │ type: 'number' │
│ }) │ │ } │
│ ) │ │ }, │
│ │ │ required: ['x', 'y'] │
│ │ │ } │
│ │ │ │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Pick( │ type T = Pick<{ │ const T = { │
│ Type.Object({ │ x: number, │ type: 'object', │
│ x: Type.Number(), │ y: number │ properties: { │
│ y: Type.Number() | }, 'x'> │ x: { │
│ }), ['x'] │ │ type: 'number' │
│ ) │ │ } │
│ │ │ }, │
│ │ │ required: ['x'] │
│ │ │ } │
│ │ │ │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Omit( │ type T = Omit<{ │ const T = { │
│ Type.Object({ │ x: number, │ type: 'object', │
│ x: Type.Number(), │ y: number │ properties: { │
│ y: Type.Number() | }, 'x'> │ y: { │
│ }), ['x'] │ │ type: 'number' │
│ ) │ │ } │
│ │ │ }, │
│ │ │ required: ['y'] │
│ │ │ } │
│ │ │ │
└────────────────────────────────┴─────────────────────────────┴────────────────────────────────┘
Modifiers
TypeBox provides modifiers that can be applied to an objects properties. This allows for optional
and readonly
to be applied to that property. The following table illustates how they map between TypeScript and JSON Schema.
┌────────────────────────────────┬─────────────────────────────┬────────────────────────────────┐
│ TypeBox │ TypeScript │ JSON Schema │
│ │ │ │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Object({ │ type T = { │ const T = { │
│ name: Type.Optional( │ name?: string │ type: 'object', │
│ Type.String() │ } │ properties: { │
│ ) │ │ name: { │
│ }) │ │ type: 'string' │
│ │ │ } │
│ │ │ } │
│ │ │ } │
│ │ │ │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Object({ │ type T = { │ const T = { │
│ name: Type.Readonly( │ readonly name: string │ type: 'object', │
│ Type.String() │ } │ properties: { │
│ ) │ │ name: { │
│ }) │ │ type: 'string' │
│ │ │ } │
│ │ │ }, │
│ │ │ required: ['name'] │
│ │ │ } │
│ │ │ │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Object({ │ type T = { │ const T = { │
│ name: Type.ReadonlyOptional( │ readonly name?: string │ type: 'object', │
│ Type.String() │ } │ properties: { │
│ ) │ │ name: { │
│ }) │ │ type: 'string' │
│ │ │ } │
│ │ │ } │
│ │ │ } │
│ │ │ │
└────────────────────────────────┴─────────────────────────────┴────────────────────────────────┘
Options
You can pass additional JSON schema options on the last argument of any given type. The following are some examples.
// string must be an email
const T = Type.String({ format: 'email' })
// number must be a multiple of 2
const T = Type.Number({ multipleOf: 2 })
// array must have at least 5 integer values
const T = Type.Array(Type.Integer(), { minItems: 5 })
Extended Types
In addition to JSON schema types, TypeBox provides several extended types that allow for function
and constructor
types to be composed. These additional types are not valid JSON Schema and will not validate using typical JSON Schema validation. However, these types can be used to frame JSON schema and describe callable interfaces that may receive JSON validated data. These types are as follows.
┌────────────────────────────────┬─────────────────────────────┬────────────────────────────────┐
│ TypeBox │ TypeScript │ Extended Schema │
│ │ │ │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Constructor([ │ type T = new ( │ const T = { │
│ Type.String(), │ arg0: string, │ type: 'constructor' │
│ Type.Number() │ arg1: number │ parameters: [{ │
│ ], Type.Boolean()) │ ) => boolean │ type: 'string' │
│ │ │ }, { │
│ │ │ type: 'number' │
│ │ │ }], │
│ │ │ return: { │
│ │ │ type: 'boolean' │
│ │ │ } │
│ │ │ } │
│ │ │ │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Function([ │ type T = ( │ const T = { │
| Type.String(), │ arg0: string, │ type : 'function', │
│ Type.Number() │ arg1: number │ parameters: [{ │
│ ], Type.Boolean()) │ ) => boolean │ type: 'string' │
│ │ │ }, { │
│ │ │ type: 'number' │
│ │ │ }], │
│ │ │ return: { │
│ │ │ type: 'boolean' │
│ │ │ } │
│ │ │ } │
│ │ │ │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Uint8Array() │ type T = Uint8Array │ const T = { │
│ │ │ type: 'object', │
│ │ │ specialized: 'Uint8Array' │
│ │ │ } │
│ │ │ │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Promise( │ type T = Promise<string> │ const T = { │
│ Type.String() │ │ type: 'promise', │
│ ) │ │ item: { │
│ │ │ type: 'string' │
│ │ │ } │
│ │ │ } │
│ │ │ │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Undefined() │ type T = undefined │ const T = { │
│ │ │ type: 'object', │
│ │ │ specialized: 'Undefined' │
│ │ │ } │
│ │ │ │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Type.Void() │ type T = void │ const T = { │
│ │ │ type: 'null' │
│ │ │ } │
│ │ │ │
└────────────────────────────────┴─────────────────────────────┴────────────────────────────────┘
Reference Types
Use Type.Ref(...)
to create referenced types. The target type must specify an $id
.
const T = Type.String({ $id: 'T' }) // const T = {
// $id: 'T',
// type: 'string'
// }
const R = Type.Ref(T) // const R = {
// $ref: 'T'
// }
Recursive Types
Use Type.Recursive(...)
to create recursive types.
const Node = Type.Recursive(Node => Type.Object({ // const Node = {
id: Type.String(), // $id: 'Node',
nodes: Type.Array(Node) // type: 'object',
}), { $id: 'Node' }) // properties: {
// id: {
// type: 'string'
// },
// nodes: {
// type: 'array',
// items: {
// $ref: 'Node'
// }
// }
// },
// required: [
// 'id',
// 'nodes'
// ]
// }
type Node = Static<typeof Node> // type Node = {
// id: string
// nodes: Node[]
// }
function test(node: Node) {
const id = node.nodes[0].nodes[0] // id is string
.nodes[0].nodes[0]
.id
}
Generic Types
Use functions to create generic types. The following creates a generic Nullable<T>
type.
import { Type, Static, TSchema } from '@sinclair/typebox'
const Nullable = <T extends TSchema>(type: T) => Type.Union([type, Type.Null()])
const T = Nullable(Type.String()) // const T = {
// anyOf: [{
// type: 'string'
// }, {
// type: 'null'
// }]
// }
type T = Static<typeof T> // type T = string | null
const U = Nullable(Type.Number()) // const U = {
// anyOf: [{
// type: 'number'
// }, {
// type: 'null'
// }]
// }
type U = Static<typeof U> // type U = number | null
Unsafe Types
Use Type.Unsafe(...)
to create custom schemas with user defined inference rules.
const T = Type.Unsafe<string>({ type: 'number' }) // const T = {
// type: 'number'
// }
type T = Static<typeof T> // type T = string
The Type.Unsafe(...)
function can be used to create schemas for validators that require specific schema representations. An example of this would be OpenAPI's nullable
and enum
schemas which are not provided by TypeBox. The following demonstrates using Type.Unsafe(...)
to create these types.
import { Type, Static, TSchema } from '@sinclair/typebox'
//--------------------------------------------------------------------------------------------
//
// Nullable<T>
//
//--------------------------------------------------------------------------------------------
function Nullable<T extends TSchema>(schema: T) {
return Type.Unsafe<Static<T> | null>({ ...schema, nullable: true })
}
const T = Nullable(Type.String()) // const T = {
// type: 'string',
// nullable: true
// }
type T = Static<typeof T> // type T = string | null
//--------------------------------------------------------------------------------------------
//
// StringEnum<string[]>
//
//--------------------------------------------------------------------------------------------
function StringEnum<T extends string[]>(values: [...T]) {
return Type.Unsafe<T[number]>({ type: 'string', enum: values })
}
const T = StringEnum(['A', 'B', 'C']) // const T = {
// enum: ['A', 'B', 'C']
// }
type T = Static<typeof T> // type T = 'A' | 'B' | 'C'
Conditional Types
Use the Conditional
module to create conditionally mapped types.
import { Conditional } from '@sinclair/typebox/conditional'
The following table shows the TypeBox mappings between TypeScript and JSON schema.
┌────────────────────────────────┬─────────────────────────────┬────────────────────────────────┐
│ TypeBox │ TypeScript │ JSON Schema │
│ │ │ │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Conditional.Extends( │ type T = │ const T = { │
│ Type.String(), │ string extends number │ const: false, │
│ Type.Number(), │ true : false │ type: 'boolean' │
│ Type.Literal(true), │ │ } │
│ Type.Literal(false) │ │ │
│ ) │ │ │
│ │ │ │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Conditional.Extract( │ type T = Extract< │ const T = { │
│ Type.Union([ │ 'a' | 'b' | 'c', │ anyOf: [{ │
│ Type.Literal('a'), │ 'a' | 'f' │ const: 'a' │
│ Type.Literal('b'), │ > │ type: 'string' │
│ Type.Literal('c') │ │ }] │
│ ]), │ │ } │
│ Type.Union([ │ │ │
│ Type.Literal('a'), │ │ │
│ Type.Literal('f') │ │ │
│ ]) │ │ │
│ ) │ │ │
│ │ │ │
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
│ const T = Conditional.Exclude( │ type T = Exclude< │ const T = { │
│ Type.Union([ │ 'a' | 'b' | 'c', │ anyOf: [{ │
│ Type.Literal('a'), │ 'a' │ const: 'b', │
│ Type.Literal('b'), │ > │ type: 'string' │
│ Type.Literal('c') │ │ }, { │
│ ]), │ │ const: 'c', │
│ Type.Union([ │ │ type: 'string' │
│ Type.Literal('a') │ │ }] │
│ ]) │ │ } │
│ ) │ │ │
│ │ │ │
└────────────────────────────────┴─────────────────────────────┴────────────────────────────────┘
Values
Use the Value
module to perform type operations on values.
import { Value } from '@sinclair/typebox/value'
const T = Type.Object({ x: Type.Number(), y: Type.Number() })
//--------------------------------------------------------------------------------------------
//
// Use Value.Create(T) to create a value from T.
//
//--------------------------------------------------------------------------------------------
const V = Value.Create(T) // const V = { x: 0, y: 0 }
//--------------------------------------------------------------------------------------------
//
// Use Value.Check(T, ...) to check if a value is of type T.
//
//--------------------------------------------------------------------------------------------
const R = Value.Check(T, { x: 1 }) // const R = false
//--------------------------------------------------------------------------------------------
//
// Use Value.Cast(T, ...) to immutably cast a value into T.
//
//--------------------------------------------------------------------------------------------
const A = Value.Cast(T, null) // const A = { x: 0, y: 0 }
const B = Value.Cast(T, { x: 1 }) // const B = { x: 1, y: 0 }
const C = Value.Cast(T, { x: 1, y: 2, z: 3 }) // const C = { x: 1, y: 2 }
Guards
Use the TypeGuard
module to test if values are valid TypeBox types.
import { TypeGuard } from '@sinclair/typebox/guard'
const T = Type.String()
if(TypeGuard.TString(T)) {
// T is TString
}
Strict
TypeBox schemas contain the Kind
and Modifier
symbol properties. These properties are provided to enable runtime type reflection on schemas, as well as helping TypeBox internally compose types. These properties are not strictly valid JSON schema; so in some cases it may be desirable to omit them. TypeBox provides a Type.Strict()
function that will omit these properties if necessary.
const T = Type.Object({ // const T = {
name: Type.Optional(Type.String()) // [Kind]: 'Object',
}) // type: 'object',
// properties: {
// name: {
// [Kind]: 'String',
// type: 'string',
// [Modifier]: 'Optional'
// }
// }
// }
const U = Type.Strict(T) // const U = {
// type: 'object',
// properties: {
// name: {
// type: 'string'
// }
// }
// }
Validation
TypeBox schemas target JSON Schema draft 6 so any validator capable of draft 6 should be fine. A good library to use for validation in JavaScript environments is Ajv. The following example shows setting up Ajv to work with TypeBox.
$ npm install ajv ajv-formats --save
//--------------------------------------------------------------------------------------------
//
// Import TypeBox and Ajv
//
//--------------------------------------------------------------------------------------------
import { Type } from '@sinclair/typebox'
import addFormats from 'ajv-formats'
import Ajv from 'ajv'
//--------------------------------------------------------------------------------------------
//
// Setup Ajv validator with the following options and formats
//
//--------------------------------------------------------------------------------------------
const ajv = addFormats(new Ajv({}), [
'date-time',
'time',
'date',
'email',
'hostname',
'ipv4',
'ipv6',
'uri',
'uri-reference',
'uuid',
'uri-template',
'json-pointer',
'relative-json-pointer',
'regex'
])
//--------------------------------------------------------------------------------------------
//
// Create a TypeBox type
//
//--------------------------------------------------------------------------------------------
const T = Type.Object({
x: Type.Number(),
y: Type.Number(),
z: Type.Number()
})
//--------------------------------------------------------------------------------------------
//
// Validate Data
//
//--------------------------------------------------------------------------------------------
const R = ajv.validate(T, { x: 1, y: 2, z: 3 }) // const R = true
Please refer to the official Ajv documentation for additional information on using Ajv.
Compiler
TypeBox provides an optional high performance runtime compiler and type checker that can be used in applications that require extremely fast validation. This compiler is optimized for TypeBox types whose schematics are known in advance. If defining custom types with Type.Unsafe<T>
please consider Ajv.
import { TypeCompiler } from '@sinclair/typebox/compiler'
Use the Compile(...)
function to compile a type.
const C = TypeCompiler.Compile(Type.Object({ // const C: TypeCheck<TObject<{
x: Type.Number(), // x: TNumber;
y: Type.Number(), // y: TNumber;
z: Type.Number() // z: TNumber;
})) // }>>
const R = C.Check({ x: 1, y: 2, z: 3 }) // const R = true
Validation errors can be read with the Errors(...)
function.
const C = TypeCompiler.Compile(Type.Object({ // const C: TypeCheck<TObject<{
x: Type.Number(), // x: TNumber;
y: Type.Number(), // y: TNumber;
z: Type.Number() // z: TNumber;
})) // }>>
const value = { }
const errors = [...C.Errors(value)] // const errors = [{
// schema: { type: 'number' },
// path: '/x',
// value: undefined,
// message: 'Expected number'
// }, {
// schema: { type: 'number' },
// path: '/y',
// value: undefined,
// message: 'Expected number'
// }, {
// schema: { type: 'number' },
// path: '/z',
// value: undefined,
// message: 'Expected number'
// }]
Compiled routines can be inspected with the .Code()
function.
const C = TypeCompiler.Compile(Type.String()) // const C: TypeCheck<TString>
console.log(C.Code()) // return function check(value) {
// return (
// (typeof value === 'string')
// )
// }
Benchmark
This project maintains a set of benchmarks that measure Ajv and TypeCompiler compilation and validation performance. These benchmarks can be run locally by cloning this repository and running npm run benchmark
. The results below show for Ajv version 8.11.0.
For additional comparative benchmarks, please refer to typescript-runtime-type-benchmarks.
Compile
This benchmark measures compilation performance for varying types. You can review this benchmark here.
┌──────────────────┬────────────┬──────────────┬──────────────┬──────────────┐
│ (index) │ Iterations │ Ajv │ TypeCompiler │ Performance │
├──────────────────┼────────────┼──────────────┼──────────────┼──────────────┤
│ Number │ 2000 │ ' 394 ms' │ ' 9 ms' │ ' 43.78 x' │
│ String │ 2000 │ ' 320 ms' │ ' 9 ms' │ ' 35.56 x' │
│ Boolean │ 2000 │ ' 326 ms' │ ' 6 ms' │ ' 54.33 x' │
│ Null │ 2000 │ ' 256 ms' │ ' 6 ms' │ ' 42.67 x' │
│ RegEx │ 2000 │ ' 494 ms' │ ' 12 ms' │ ' 41.17 x' │
│ ObjectA │ 2000 │ ' 2813 ms' │ ' 41 ms' │ ' 68.61 x' │
│ ObjectB │ 2000 │ ' 2949 ms' │ ' 30 ms' │ ' 98.30 x' │
│ Tuple │ 2000 │ ' 1258 ms' │ ' 19 ms' │ ' 66.21 x' │
│ Union │ 2000 │ ' 1308 ms' │ ' 22 ms' │ ' 59.45 x' │
│ Vector4 │ 2000 │ ' 1589 ms' │ ' 17 ms' │ ' 93.47 x' │
│ Matrix4 │ 2000 │ ' 932 ms' │ ' 11 ms' │ ' 84.73 x' │
│ Literal_String │ 2000 │ ' 343 ms' │ ' 6 ms' │ ' 57.17 x' │
│ Literal_Number │ 2000 │ ' 380 ms' │ ' 6 ms' │ ' 63.33 x' │
│ Literal_Boolean │ 2000 │ ' 369 ms' │ ' 4 ms' │ ' 92.25 x' │
│ Array_Number │ 2000 │ ' 730 ms' │ ' 6 ms' │ ' 121.67 x' │
│ Array_String │ 2000 │ ' 764 ms' │ ' 7 ms' │ ' 109.14 x' │
│ Array_Boolean │ 2000 │ ' 791 ms' │ ' 8 ms' │ ' 98.88 x' │
│ Array_ObjectA │ 2000 │ ' 3550 ms' │ ' 33 ms' │ ' 107.58 x' │
│ Array_ObjectB │ 2000 │ ' 3709 ms' │ ' 33 ms' │ ' 112.39 x' │
│ Array_Tuple │ 2000 │ ' 2209 ms' │ ' 15 ms' │ ' 147.27 x' │
│ Array_Union │ 2000 │ ' 1733 ms' │ ' 18 ms' │ ' 96.28 x' │
│ Array_Vector4 │ 2000 │ ' 2279 ms' │ ' 16 ms' │ ' 142.44 x' │
│ Array_Matrix4 │ 2000 │ ' 1587 ms' │ ' 11 ms' │ ' 144.27 x' │
└──────────────────┴────────────┴──────────────┴──────────────┴──────────────┘
Validate
This benchmark measures validation performance for varying types. You can review this benchmark here.
┌──────────────────┬────────────┬──────────────┬──────────────┬──────────────┬──────────────┐
│ (index) │ Iterations │ ValueCheck │ Ajv │ TypeCompiler │ Performance │
├──────────────────┼────────────┼──────────────┼──────────────┼──────────────┼──────────────┤
│ Number │ 1000000 │ ' 27 ms' │ ' 6 ms' │ ' 4 ms' │ ' 1.50 x' │
│ String │ 1000000 │ ' 23 ms' │ ' 20 ms' │ ' 11 ms' │ ' 1.82 x' │
│ Boolean │ 1000000 │ ' 21 ms' │ ' 19 ms' │ ' 10 ms' │ ' 1.90 x' │
│ Null │ 1000000 │ ' 24 ms' │ ' 18 ms' │ ' 10 ms' │ ' 1.80 x' │
│ RegEx │ 1000000 │ ' 170 ms' │ ' 43 ms' │ ' 36 ms' │ ' 1.19 x' │
│ ObjectA │ 1000000 │ ' 567 ms' │ ' 34 ms' │ ' 23 ms' │ ' 1.48 x' │
│ ObjectB │ 1000000 │ ' 985 ms' │ ' 50 ms' │ ' 36 ms' │ ' 1.39 x' │
│ Tuple │ 1000000 │ ' 119 ms' │ ' 24 ms' │ ' 14 ms' │ ' 1.71 x' │
│ Union │ 1000000 │ ' 302 ms' │ ' 26 ms' │ ' 14 ms' │ ' 1.86 x' │
│ Recursive │ 1000000 │ ' 3071 ms' │ ' 397 ms' │ ' 177 ms' │ ' 2.24 x' │
│ Vector4 │ 1000000 │ ' 135 ms' │ ' 24 ms' │ ' 11 ms' │ ' 2.18 x' │
│ Matrix4 │ 1000000 │ ' 632 ms' │ ' 41 ms' │ ' 30 ms' │ ' 1.37 x' │
│ Literal_String │ 1000000 │ ' 49 ms' │ ' 19 ms' │ ' 9 ms' │ ' 2.11 x' │
│ Literal_Number │ 1000000 │ ' 56 ms' │ ' 18 ms' │ ' 9 ms' │ ' 2.00 x' │
│ Literal_Boolean │ 1000000 │ ' 56 ms' │ ' 19 ms' │ ' 9 ms' │ ' 2.11 x' │
│ Array_Number │ 1000000 │ ' 408 ms' │ ' 31 ms' │ ' 17 ms' │ ' 1.82 x' │
│ Array_String │ 1000000 │ ' 458 ms' │ ' 32 ms' │ ' 20 ms' │ ' 1.60 x' │
│ Array_Boolean │ 1000000 │ ' 431 ms' │ ' 34 ms' │ ' 24 ms' │ ' 1.42 x' │
│ Array_ObjectA │ 1000000 │ ' 13322 ms' │ ' 2549 ms' │ ' 1636 ms' │ ' 1.56 x' │
│ Array_ObjectB │ 1000000 │ ' 16341 ms' │ ' 2865 ms' │ ' 2074 ms' │ ' 1.38 x' │
│ Array_Tuple │ 1000000 │ ' 1640 ms' │ ' 92 ms' │ ' 71 ms' │ ' 1.30 x' │
│ Array_Union │ 1000000 │ ' 4803 ms' │ ' 237 ms' │ ' 89 ms' │ ' 2.66 x' │
│ Array_Recursive │ 1000000 │ ' 53759 ms' │ ' 7694 ms' │ ' 2600 ms' │ ' 2.96 x' │
│ Array_Vector4 │ 1000000 │ ' 2099 ms' │ ' 96 ms' │ ' 52 ms' │ ' 1.85 x' │
│ Array_Matrix4 │ 1000000 │ ' 11436 ms' │ ' 384 ms' │ ' 310 ms' │ ' 1.24 x' │
└──────────────────┴────────────┴──────────────┴──────────────┴──────────────┴──────────────┘
Compression
The following table lists esbuild compiled and minified sizes for each TypeBox import.
┌──────────────────────┬────────────┬────────────┬─────────────┐
│ (index) │ Compiled │ Minified │ Compression │
├──────────────────────┼────────────┼────────────┼─────────────┤
│ typebox/compiler │ ' 47 kb' │ ' 23 kb' │ '1.99 x' │
│ typebox/conditional │ ' 41 kb' │ ' 16 kb' │ '2.46 x' │
│ typebox/guard │ ' 20 kb' │ ' 9 kb' │ '2.06 x' │
│ typebox/value │ ' 54 kb' │ ' 25 kb' │ '2.14 x' │
│ typebox │ ' 11 kb' │ ' 5 kb' │ '1.89 x' │
└──────────────────────┴────────────┴────────────┴─────────────┘
Contribute
TypeBox is open to community contribution. Please ensure you submit an open issue before submitting your pull request. The TypeBox project preferences open community discussion prior to accepting new features.