TypeBox

JSON Schema Type Builder with Static Type Resolution for TypeScript



[![npm version](https://badge.fury.io/js/%40sinclair%2Ftypebox.svg)](https://badge.fury.io/js/%40sinclair%2Ftypebox) [![Downloads](https://img.shields.io/npm/dm/%40sinclair%2Ftypebox.svg)](https://www.npmjs.com/package/%40sinclair%2Ftypebox) [![GitHub CI](https://github.com/sinclairzx81/typebox/workflows/GitHub%20CI/badge.svg)](https://github.com/sinclairzx81/typebox/actions)
## Install Node ```bash $ npm install @sinclair/typebox --save ``` Deno and ESM ```typescript import { Static, Type } from 'https://esm.sh/@sinclair/typebox' ``` ## Example ```typescript import { Static, Type } from '@sinclair/typebox' const T = Type.String() // const T = { type: 'string' } type T = Static // 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 a unified type that can be statically checked by TypeScript and runtime asserted using standard JSON Schema validation. TypeBox is designed to enable JSON schema to compose with the same flexibility as TypeScript's type system. It can be used either as a simple tool to build up complex schemas or integrated into REST and RPC services to help validate data received over the wire. License MIT ## Contents - [Install](#install) - [Overview](#overview) - [Usage](#usage) - [Types](#types) - [Modifiers](#modifiers) - [Options](#options) - [Extended Types](#extended-types) - [Reference Types](#reference-types) - [Recursive Types](#recursive-types) - [Generic Types](#generic-types) - [Unsafe Types](#unsafe-types) - [Conditional Types](#conditional-types) - [Values](#values) - [Guards](#guards) - [Strict](#strict) - [Validation](#validation) - [Compiler](#compiler) - [Benchmark](#benchmark) - [Contribute](#contribute) ## Usage The following demonstrates TypeBox's general usage. ```typescript 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 // 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. ```typescript ┌────────────────────────────────┬─────────────────────────────┬────────────────────────────────┐ │ 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. ```typescript ┌────────────────────────────────┬─────────────────────────────┬────────────────────────────────┐ │ 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. ```typescript // 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. ```typescript ┌────────────────────────────────┬─────────────────────────────┬────────────────────────────────┐ │ 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 │ 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`. ```typescript 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. ```typescript 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 // 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` type. ```typescript import { Type, Static, TSchema } from '@sinclair/typebox' const Nullable = (type: T) => Type.Union([type, Type.Null()]) const T = Nullable(Type.String()) // const T = { // anyOf: [{ // type: 'string' // }, { // type: 'null' // }] // } type T = Static // type T = string | null const U = Nullable(Type.Number()) // const U = { // anyOf: [{ // type: 'number' // }, { // type: 'null' // }] // } type U = Static // type U = number | null ``` ## Unsafe Types Use `Type.Unsafe(...)` to create custom schemas with user defined inference rules. ```typescript const T = Type.Unsafe({ type: 'number' }) // const T = { // type: 'number' // } type T = Static // type T = string ``` This function can be used to create custom schemas for validators that require specific schema representations. An example of this might be OpenAPI's `nullable` and `enum` schemas which are not provided by TypeBox. The following demonstrates using `Type.Unsafe(...)` to create these types. ```typescript import { Type, Static, TSchema } from '@sinclair/typebox' //-------------------------------------------------------------------------------------------- // // Nullable // //-------------------------------------------------------------------------------------------- function Nullable(schema: T) { return Type.Unsafe | null>({ ...schema, nullable: true }) } const T = Nullable(Type.String()) // const T = { // type: 'string', // nullable: true // } type T = Static // type T = string | null //-------------------------------------------------------------------------------------------- // // StringEnum // //-------------------------------------------------------------------------------------------- function StringEnum(values: [...T]) { return Type.Unsafe({ type: 'string', enum: values }) } const T = StringEnum(['A', 'B', 'C']) // const T = { // enum: ['A', 'B', 'C'] // } type T = Static // type T = 'A' | 'B' | 'C' ``` ## Conditional Types Use the conditional module to create [Conditional Types](https://www.typescriptlang.org/docs/handbook/2/conditional-types.html). This module implements TypeScript's structural equivalence checks to enable TypeBox types to be conditionally inferred at runtime. This module also provides the [Extract](https://www.typescriptlang.org/docs/handbook/utility-types.html#extracttype-union) and [Exclude](https://www.typescriptlang.org/docs/handbook/utility-types.html#excludeuniontype-excludedmembers) utility types which are expressed as conditional types in TypeScript. The conditional module is provided as an optional import. ```typescript import { Conditional } from '@sinclair/typebox/conditional' ``` The following table shows the TypeBox mappings between TypeScript and JSON schema. ```typescript ┌────────────────────────────────┬─────────────────────────────┬────────────────────────────────┐ │ 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 common type operations on values. This module provides functionality to create, check and cast values into a given type. Note that this module internally uses dynamic type checking to perform these operations. For faster type checking performance, consider using either Ajv or the TypeBox [TypeCompiler](#compiler). The value module is provided as an optional import. ```typescript import { Value } from '@sinclair/typebox/value' ``` The following demonstrates its use. ```typescript const T = Type.Object({ x: Type.Number(), y: Type.Number() }, { additionalProperties: false }) //-------------------------------------------------------------------------------------------- // // 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 guard module to test if values are TypeBox types. ```typescript 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. ```typescript 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](https://www.npmjs.com/package/ajv). The following example shows setting up Ajv to work with TypeBox. ```bash $ npm install ajv ajv-formats --save ``` ```typescript //-------------------------------------------------------------------------------------------- // // 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](https://ajv.js.org/guide/getting-started.html) for additional information on using Ajv. ## Compiler TypeBox provides an optional high performance just-in-time (JIT) compiler and type checker that can be used in applications that require extremely fast validation. Note that this compiler is optimized for TypeBox types only where the schematics are known in advance. If defining custom types with `Type.Unsafe` please consider Ajv. The compiler module is provided as an optional import. ```typescript import { TypeCompiler } from '@sinclair/typebox/compiler' ``` Use the `Compile(...)` function to compile a type. ```typescript const C = TypeCompiler.Compile(Type.Object({ // const C: TypeCheck> const R = C.Check({ x: 1, y: 2, z: 3 }) // const R = true ``` Validation errors can be read with the `Errors(...)` function. ```typescript const C = TypeCompiler.Compile(Type.Object({ // const C: TypeCheck> 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. ```typescript const C = TypeCompiler.Compile(Type.String()) // const C: TypeCheck console.log(C.Code()) // return function check(value) { // return ( // (typeof value === 'string') // ) // } ``` ## Benchmark This project maintains a set of benchmarks that measure Ajv, Value 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](https://moltar.github.io/typescript-runtime-type-benchmarks/). ### Compile This benchmark measures compilation performance for varying types. You can review this benchmark [here](https://github.com/sinclairzx81/typebox/blob/master/benchmark/measurement/module/compile.ts). ```typescript ┌──────────────────┬────────────┬──────────────┬──────────────┬──────────────┐ │ (index) │ Iterations │ Ajv │ TypeCompiler │ Performance │ ├──────────────────┼────────────┼──────────────┼──────────────┼──────────────┤ │ Number │ 2000 │ ' 399 ms' │ ' 9 ms' │ ' 44.33 x' │ │ String │ 2000 │ ' 306 ms' │ ' 8 ms' │ ' 38.25 x' │ │ Boolean │ 2000 │ ' 315 ms' │ ' 5 ms' │ ' 63.00 x' │ │ Null │ 2000 │ ' 255 ms' │ ' 6 ms' │ ' 42.50 x' │ │ RegEx │ 2000 │ ' 478 ms' │ ' 10 ms' │ ' 47.80 x' │ │ ObjectA │ 2000 │ ' 2850 ms' │ ' 39 ms' │ ' 73.08 x' │ │ ObjectB │ 2000 │ ' 3027 ms' │ ' 34 ms' │ ' 89.03 x' │ │ Tuple │ 2000 │ ' 1374 ms' │ ' 27 ms' │ ' 50.89 x' │ │ Union │ 2000 │ ' 1307 ms' │ ' 22 ms' │ ' 59.41 x' │ │ Vector4 │ 2000 │ ' 1568 ms' │ ' 17 ms' │ ' 92.24 x' │ │ Matrix4 │ 2000 │ ' 911 ms' │ ' 11 ms' │ ' 82.82 x' │ │ Literal_String │ 2000 │ ' 332 ms' │ ' 8 ms' │ ' 41.50 x' │ │ Literal_Number │ 2000 │ ' 363 ms' │ ' 8 ms' │ ' 45.38 x' │ │ Literal_Boolean │ 2000 │ ' 360 ms' │ ' 5 ms' │ ' 72.00 x' │ │ Array_Number │ 2000 │ ' 704 ms' │ ' 6 ms' │ ' 117.33 x' │ │ Array_String │ 2000 │ ' 745 ms' │ ' 11 ms' │ ' 67.73 x' │ │ Array_Boolean │ 2000 │ ' 781 ms' │ ' 7 ms' │ ' 111.57 x' │ │ Array_ObjectA │ 2000 │ ' 3552 ms' │ ' 31 ms' │ ' 114.58 x' │ │ Array_ObjectB │ 2000 │ ' 3738 ms' │ ' 33 ms' │ ' 113.27 x' │ │ Array_Tuple │ 2000 │ ' 2336 ms' │ ' 16 ms' │ ' 146.00 x' │ │ Array_Union │ 2000 │ ' 1758 ms' │ ' 20 ms' │ ' 87.90 x' │ │ Array_Vector4 │ 2000 │ ' 2305 ms' │ ' 16 ms' │ ' 144.06 x' │ │ Array_Matrix4 │ 2000 │ ' 1635 ms' │ ' 11 ms' │ ' 148.64 x' │ └──────────────────┴────────────┴──────────────┴──────────────┴──────────────┘ ``` ### Validate This benchmark measures validation performance for varying types. You can review this benchmark [here](https://github.com/sinclairzx81/typebox/blob/master/benchmark/measurement/module/check.ts). ```typescript ┌──────────────────┬────────────┬──────────────┬──────────────┬──────────────┬──────────────┐ │ (index) │ Iterations │ ValueCheck │ Ajv │ TypeCompiler │ Performance │ ├──────────────────┼────────────┼──────────────┼──────────────┼──────────────┼──────────────┤ │ Number │ 1000000 │ ' 31 ms' │ ' 6 ms' │ ' 5 ms' │ ' 1.20 x' │ │ String │ 1000000 │ ' 26 ms' │ ' 22 ms' │ ' 12 ms' │ ' 1.83 x' │ │ Boolean │ 1000000 │ ' 21 ms' │ ' 22 ms' │ ' 11 ms' │ ' 2.00 x' │ │ Null │ 1000000 │ ' 25 ms' │ ' 24 ms' │ ' 9 ms' │ ' 2.67 x' │ │ RegEx │ 1000000 │ ' 166 ms' │ ' 45 ms' │ ' 38 ms' │ ' 1.18 x' │ │ ObjectA │ 1000000 │ ' 535 ms' │ ' 36 ms' │ ' 22 ms' │ ' 1.64 x' │ │ ObjectB │ 1000000 │ ' 957 ms' │ ' 49 ms' │ ' 37 ms' │ ' 1.32 x' │ │ Tuple │ 1000000 │ ' 112 ms' │ ' 24 ms' │ ' 14 ms' │ ' 1.71 x' │ │ Union │ 1000000 │ ' 304 ms' │ ' 25 ms' │ ' 14 ms' │ ' 1.79 x' │ │ Recursive │ 1000000 │ ' 2986 ms' │ ' 391 ms' │ ' 164 ms' │ ' 2.38 x' │ │ Vector4 │ 1000000 │ ' 145 ms' │ ' 22 ms' │ ' 13 ms' │ ' 1.69 x' │ │ Matrix4 │ 1000000 │ ' 575 ms' │ ' 39 ms' │ ' 29 ms' │ ' 1.34 x' │ │ Literal_String │ 1000000 │ ' 44 ms' │ ' 18 ms' │ ' 10 ms' │ ' 1.80 x' │ │ Literal_Number │ 1000000 │ ' 46 ms' │ ' 19 ms' │ ' 9 ms' │ ' 2.11 x' │ │ Literal_Boolean │ 1000000 │ ' 47 ms' │ ' 19 ms' │ ' 9 ms' │ ' 2.11 x' │ │ Array_Number │ 1000000 │ ' 398 ms' │ ' 30 ms' │ ' 17 ms' │ ' 1.76 x' │ │ Array_String │ 1000000 │ ' 438 ms' │ ' 30 ms' │ ' 20 ms' │ ' 1.50 x' │ │ Array_Boolean │ 1000000 │ ' 443 ms' │ ' 37 ms' │ ' 24 ms' │ ' 1.54 x' │ │ Array_ObjectA │ 1000000 │ ' 13702 ms' │ ' 2649 ms' │ ' 1673 ms' │ ' 1.58 x' │ │ Array_ObjectB │ 1000000 │ ' 16091 ms' │ ' 2964 ms' │ ' 2032 ms' │ ' 1.46 x' │ │ Array_Tuple │ 1000000 │ ' 1665 ms' │ ' 92 ms' │ ' 70 ms' │ ' 1.31 x' │ │ Array_Union │ 1000000 │ ' 4631 ms' │ ' 220 ms' │ ' 88 ms' │ ' 2.50 x' │ │ Array_Recursive │ 1000000 │ ' 53745 ms' │ ' 6891 ms' │ ' 2744 ms' │ ' 2.51 x' │ │ Array_Vector4 │ 1000000 │ ' 2156 ms' │ ' 105 ms' │ ' 55 ms' │ ' 1.91 x' │ │ Array_Matrix4 │ 1000000 │ ' 11686 ms' │ ' 388 ms' │ ' 330 ms' │ ' 1.18 x' │ └──────────────────┴────────────┴──────────────┴──────────────┴──────────────┴──────────────┘ ``` ### Compression The following table lists esbuild compiled and minified sizes for each TypeBox module. ```typescript ┌──────────────────────┬────────────┬────────────┬─────────────┐ │ (index) │ Compiled │ Minified │ Compression │ ├──────────────────────┼────────────┼────────────┼─────────────┤ │ typebox/compiler │ ' 47 kb' │ ' 23 kb' │ '1.99 x' │ │ typebox/conditional │ ' 41 kb' │ ' 16 kb' │ '2.47 x' │ │ typebox/guard │ ' 20 kb' │ ' 9 kb' │ '2.08 x' │ │ typebox/value │ ' 55 kb' │ ' 25 kb' │ '2.15 x' │ │ typebox │ ' 11 kb' │ ' 5 kb' │ '1.91 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.