@serverless-seoul/typebox
Advanced tools
Comparing version 1.1.0 to 2.0.0
@@ -0,1 +1,23 @@ | ||
# [2.0.0](https://github.com/catchfashion/typebox/compare/v1.1.0...v2.0.0) (2021-07-06) | ||
### Bug Fixes | ||
* Allow intersect duplicated required ([#68](https://github.com/catchfashion/typebox/issues/68)) ([e6de056](https://github.com/catchfashion/typebox/commit/e6de056eb59372dcaf839be03bf4c318c2ed9963)) | ||
### Features | ||
* merge upstream changes ([65eee4a](https://github.com/catchfashion/typebox/commit/65eee4a4e545a0f0457d4c9010a3f811b381cf73)) | ||
* Let Composition types to rely on variadic tuple types (#36) ([168c7ed](https://github.com/catchfashion/typebox/commit/168c7ed2f7592e5b05ac2e72c0f71290a2b0852f)), closes [#36](https://github.com/catchfashion/typebox/issues/36) | ||
### BREAKING CHANGES | ||
* Requires TypeScript 4 | ||
* fix: make TModifier type to non-any type | ||
# [1.1.0](https://github.com/catchfashion/typebox/compare/v1.0.4...v1.1.0) (2020-11-04) | ||
@@ -2,0 +24,0 @@ |
{ | ||
"name": "@serverless-seoul/typebox", | ||
"version": "1.1.0", | ||
"version": "2.0.0", | ||
"description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", | ||
@@ -30,6 +30,6 @@ "keywords": [ | ||
"scripts": { | ||
"clean": "rm -rf dist", | ||
"prebuild": "npm run clean", | ||
"build": "tsc -p tsconfig.build.json", | ||
"test": "mocha -r ts-node/register 'spec/index.ts'", | ||
"clean": "hammer task clean", | ||
"build": "hammer task build", | ||
"example": "hammer task example", | ||
"test": "hammer task spec", | ||
"prepublishOnly": "npm run build" | ||
@@ -41,13 +41,14 @@ }, | ||
"@semantic-release/github": "^7.1.1", | ||
"@types/ajv": "^1.0.0", | ||
"@types/chai": "^4.2.14", | ||
"@types/mocha": "^8.0.3", | ||
"@types/node": "^14.14.6", | ||
"ajv": "^6.12.6", | ||
"chai": "^4.1.2", | ||
"mocha": "^8.2.1", | ||
"@sinclair/hammer": "^0.12.1", | ||
"@types/chai": "^4.2.16", | ||
"@types/mocha": "^8.2.2", | ||
"@types/node": "^14.14.37", | ||
"ajv": "^8.1.0", | ||
"ajv-formats": "^2.0.2", | ||
"chai": "^4.3.4", | ||
"mocha": "^8.3.2", | ||
"semantic-release": "^17.2.2", | ||
"ts-node": "^9.0.0", | ||
"typescript": "^4.0.5" | ||
"typescript": "^4.1.2" | ||
} | ||
} |
1025
readme.md
@@ -7,9 +7,8 @@ <div align='center'> | ||
[![npm version](https://badge.fury.io/js/%40sinclair%2Ftypebox.svg)](https://badge.fury.io/js/%40sinclair%2Ftypebox) | ||
[![GitHub CI](https://github.com/sinclairzx81/typebox/workflows/GitHub%20CI/badge.svg)](https://github.com/sinclairzx81/typebox/actions) | ||
[![npm version](https://badge.fury.io/js/%40sinclair%2Ftypebox.svg)](https://badge.fury.io/js/%40sinclair%2Ftypebox) [![GitHub CI](https://github.com/sinclairzx81/typebox/workflows/GitHub%20CI/badge.svg)](https://github.com/sinclairzx81/typebox/actions) | ||
<img src='./doc/example.gif'></img> | ||
</div> | ||
<a name="Install"></a> | ||
@@ -23,2 +22,12 @@ | ||
## Usage | ||
```typescript | ||
import { Static, Type } from '@sinclair/typebox' | ||
const T = Type.String() // const T = { "type": "string" } | ||
type T = Static<typeof T> // type T = string | ||
``` | ||
<a name="Overview"></a> | ||
@@ -28,7 +37,7 @@ | ||
TypeBox is a type builder library that allows developers to compose in-memory JSON Schema objects that can be statically resolved to TypeScript types. The schemas produced by TypeBox can be used directly as validation schemas or reflected upon by navigating the standard JSON Schema properties at runtime. 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. | ||
TypeBox is a type builder library that creates in-memory JSON Schema objects that can be statically resolved to TypeScript types. The schemas produced by this library are built to match the static type checking rules of the TypeScript compiler. TypeBox allows one to create a single unified type that can be both statically checked by the TypeScript compiler and runtime asserted using standard JSON schema validation. | ||
TypeBox does not provide any mechanism for validating JSON Schema. Please refer to libraries such as [AJV](https://www.npmjs.com/package/ajv) or similar to validate the schemas created with this library. | ||
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. TypeBox does not provide any JSON schema validation. Please use libraries such as [AJV](https://www.npmjs.com/package/ajv) to validate schemas built with this library. | ||
Requires TypeScript 3.8.3 and above. | ||
Requires TypeScript 4.0.3 and above. | ||
@@ -42,551 +51,617 @@ License MIT | ||
- [Types](#Types) | ||
- [Function Types](#Contracts) | ||
- [Functions](#Functions) | ||
- [Generics](#Generics) | ||
- [Modifiers](#Modifiers) | ||
- [Options](#Options) | ||
- [Extended Types](#Extended-Types) | ||
- [Reference Types](#Reference-Types) | ||
- [Strict](#Strict) | ||
- [Interfaces](#Interfaces) | ||
- [Validation](#Validation) | ||
<a name="Example"></a> | ||
## Example | ||
The following shows the general usage. | ||
The following demonstrates TypeBox's general usage. | ||
```typescript | ||
import { Type, Static } from '@sinclair/typebox' | ||
// some type ... | ||
//-------------------------------------------------------------------------------------------- | ||
// | ||
// Let's say you have the following type ... | ||
// | ||
//-------------------------------------------------------------------------------------------- | ||
type Order = { | ||
email: string, | ||
address: string, | ||
quantity: number, | ||
option: 'pizza' | 'salad' | 'pie' | ||
type Record = { | ||
id: string, | ||
name: string, | ||
timestamp: number | ||
} | ||
// ... can be expressed as ... | ||
//-------------------------------------------------------------------------------------------- | ||
// | ||
// ... you can express this type in the following way. | ||
// | ||
//-------------------------------------------------------------------------------------------- | ||
const Order = Type.Object({ | ||
email: Type.String({ format: 'email' }), | ||
address: Type.String(), | ||
quantity: Type.Number({ minimum: 1, maximum: 99 }), | ||
option: Type.Union([ | ||
Type.Literal('pizza'), | ||
Type.Literal('salad'), | ||
Type.Literal('pie') | ||
]) | ||
}) | ||
const Record = Type.Object({ // const Record = { | ||
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" | ||
// ] | ||
// } | ||
// ... which can be reflected | ||
//-------------------------------------------------------------------------------------------- | ||
// | ||
// ... then infer back to the original static type this way. | ||
// | ||
//-------------------------------------------------------------------------------------------- | ||
console.log(JSON.stringify(Order, null, 2)) | ||
type Record = Static<typeof Record> // type Record = { | ||
// id: string, | ||
// name: string, | ||
// timestamp: number | ||
// } | ||
// ... and statically resolved | ||
//-------------------------------------------------------------------------------------------- | ||
// | ||
// ... then use the type both as JSON schema and as a TypeScript type. | ||
// | ||
//-------------------------------------------------------------------------------------------- | ||
type TOrder = Static<typeof Order> | ||
function receive(record: Record) { // ... as a type | ||
if(JSON.validate(Record, record)) { // ... as a schema | ||
// ok... | ||
} | ||
} | ||
// .. and validated as JSON Schema | ||
JSON.validate(Order, { // IETF | TC39 ? | ||
email: 'dave@domain.com', | ||
address: '...', | ||
quantity: 99, | ||
option: 'pie' | ||
}) | ||
// ... and so on ... | ||
``` | ||
<a href='Types'></a> | ||
<a name="Types"></a> | ||
## Types | ||
TypeBox provides a number of functions to generate JSON Schema data types. The following tables list the functions TypeBox provides and their respective TypeScript and JSON Schema equivalents. | ||
The following table outlines the TypeBox mappings between TypeScript and JSON schema. | ||
### TypeBox > TypeScript | ||
```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.Dict( │ type T = { │ const T = { │ | ||
│ Type.Number() │ [key: string] │ type: 'object' │ | ||
│ ) │ } : number │ additionalProperties: { │ | ||
│ │ │ 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, │ enum: [0, 1], │ | ||
│ B │ B │ type: 'number' │ | ||
│ } │ } │ } │ | ||
│ │ │ │ | ||
│ type T = Type.Enum(Foo) │ type T = Foo │ │ | ||
│ │ │ │ | ||
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ | ||
│ const T = Type.Union([ │ type T = string | number │ const T = { │ | ||
│ Type.String(), │ │ anyOf: [{ │ | ||
│ Type.Number() │ │ type: 'string' │ | ||
│ ]) │ │ }, { │ | ||
│ │ │ type: 'number' │ | ||
│ │ │ }] │ | ||
│ │ │ } │ | ||
│ │ │ │ | ||
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ | ||
│ const T = Type.KeyOf( │ type T = keyof { │ const T = { │ | ||
│ Type.Object({ │ x: number, │ enum: ['x', 'y'], │ | ||
│ x: Type.Number(), │ y: number │ type: 'string' │ | ||
│ y: 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.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'] │ | ||
│ │ │ } │ | ||
│ │ │ │ | ||
└────────────────────────────────┴─────────────────────────────┴────────────────────────────────┘ | ||
``` | ||
<a name="Modifiers"></a> | ||
<table> | ||
<thead> | ||
<tr> | ||
<th>Type</th> | ||
<th>TypeBox</th> | ||
<th>TypeScript</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
<tr> | ||
<td>Literal</td> | ||
<td><code>const T = Type.Literal(123)</code></td> | ||
<td><code>type T = 123</code></td> | ||
</tr> | ||
<tr> | ||
<td>String</td> | ||
<td><code>const T = Type.String()</code></td> | ||
<td><code>type T = string</code></td> | ||
</tr> | ||
<tr> | ||
<td>Number</td> | ||
<td><code>const T = Type.Number()</code></td> | ||
<td><code>type T = number</code></td> | ||
</tr> | ||
<tr> | ||
<td>Integer</td> | ||
<td><code>const T = Type.Integer()</code></td> | ||
<td><code>type T = number</code></td> | ||
</tr> | ||
<tr> | ||
<td>Boolean</td> | ||
<td><code>const T = Type.Boolean()</code></td> | ||
<td><code>type T = boolean</code></td> | ||
</tr> | ||
<tr> | ||
<td>Object</td> | ||
<td><code>const T = Type.Object({ name: Type.String() })</code></td> | ||
<td><code>type T = { name: string }</code></td> | ||
</tr> | ||
<tr> | ||
<td>Array</td> | ||
<td><code>const T = Type.Array(Type.Number())</code></td> | ||
<td><code>type T = number[]</code></td> | ||
</tr> | ||
<tr> | ||
<td>Map</td> | ||
<td><code>const T = Type.Map(Type.Number())</code></td> | ||
<td><code>type T = { [key: string] } : number</code></td> | ||
</tr> | ||
<tr> | ||
<td>Intersect</td> | ||
<td><code>const T = Type.Intersect([Type.String(), Type.Number()])</code></td> | ||
<td><code>type T = string & number</code></td> | ||
</tr> | ||
<tr> | ||
<td>Union</td> | ||
<td><code>const T = Type.Union([Type.String(), Type.Number()])</code></td> | ||
<td><code>type T = string | number</code></td> | ||
</tr> | ||
<tr> | ||
<td>Tuple</td> | ||
<td><code>const T = Type.Tuple([Type.String(), Type.Number()])</code></td> | ||
<td><code>type T = [string, number]</code></td> | ||
</tr> | ||
<tr> | ||
<td>Any</td> | ||
<td><code>const T = Type.Any()</code></td> | ||
<td><code>type T = any</code></td> | ||
</tr> | ||
<tr> | ||
<td>Null</td> | ||
<td><code>const T = Type.Null()</code></td> | ||
<td><code>type T = null</code></td> | ||
</tr> | ||
<tr> | ||
<td>Pattern</td> | ||
<td><code>const T = Type.Pattern(/foo/)</code></td> | ||
<td><code>type T = string</code></td> | ||
</tr> | ||
<tr> | ||
<td>Guid</td> | ||
<td><code>const T = Type.Guid()</code></td> | ||
<td><code>type T = string</code></td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
### Modifiers | ||
### TypeBox > JSON Schema | ||
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. | ||
<table> | ||
<thead> | ||
<tr> | ||
<th>Type</th> | ||
<th>TypeBox</th> | ||
<th>JSON Schema</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
<tr> | ||
<td>Literal</td> | ||
<td><code>const T = Type.Literal(123)</code></td> | ||
<td><code>{ type: 'number', enum: [123] }</code></td> | ||
</tr> | ||
<tr> | ||
<td>String</td> | ||
<td><code>const T = Type.String()</code></td> | ||
<td><code>{ type: 'string' }</code></td> | ||
</tr> | ||
<tr> | ||
<td>Number</td> | ||
<td><code>const T = Type.Number()</code></td> | ||
<td><code>{ type: 'number' }</code></td> | ||
</tr> | ||
<tr> | ||
<td>Integer</td> | ||
<td><code>const T = Type.Number()</code></td> | ||
<td><code>{ type: 'integer' }</code></td> | ||
</tr> | ||
<tr> | ||
<td>Boolean</td> | ||
<td><code>const T = Type.Boolean()</code></td> | ||
<td><code>{ type: 'boolean' }</code></td> | ||
</tr> | ||
<tr> | ||
<td>Object</td> | ||
<td><code>const T = Type.Object({ name: Type: String() })</code></td> | ||
<td><code>{ type: 'object': properties: { name: { type: 'string' } }, required: ['name'] }</code></td> | ||
</tr> | ||
<tr> | ||
<td>Array</td> | ||
<td><code>const T = Type.Array(Type.String())</code></td> | ||
<td><code>{ type: 'array': items: { type: 'string' } }</code></td> | ||
</tr> | ||
<tr> | ||
<td>Map</td> | ||
<td><code>const T = Type.Map(Type.Number())</code></td> | ||
<td><code>{ type: 'object', additionalProperties: { type: 'number' } }</code></td> | ||
</tr> | ||
<tr> | ||
<td>Intersect</td> | ||
<td><code>const T = Type.Intersect([Type.Number(), Type.String()])</code></td> | ||
<td><code>{ allOf: [{ type: 'number'}, {type: 'string'}] }</code></td> | ||
</tr> | ||
<tr> | ||
<td>Union</td> | ||
<td><code>const T = Type.Union([Type.Number(), Type.String()])</code></td> | ||
<td><code>{ oneOf: [{ type: 'number'}, {type: 'string'}] }</code></td> | ||
</tr> | ||
<tr> | ||
<td>Tuple</td> | ||
<td><code>const T = Type.Tuple([Type.Number(), Type.String()])</code></td> | ||
<td><code>{ type: "array", items: [{type: 'string'}, {type: 'number'}], additionalItems: false, minItems: 2, maxItems: 2 }</code></td> | ||
</tr> | ||
<tr> | ||
<td>Any</td> | ||
<td><code>const T = Type.Any()</code></td> | ||
<td><code>{ }</code></td> | ||
</tr> | ||
<tr> | ||
<td>Null</td> | ||
<td><code>const T = Type.Null()</code></td> | ||
<td><code>{ type: 'null' }</code></td> | ||
</tr> | ||
<tr> | ||
<td>Pattern</td> | ||
<td><code>const T = Type.Pattern(/foo/)</code></td> | ||
<td><code>{ type: 'string', pattern: 'foo' }</code></td> | ||
</tr> | ||
<tr> | ||
<td>Guid</td> | ||
<td><code>const T = Type.Guid()</code></td> | ||
<td><code>{ type: 'string', pattern: '<guid-regex>' }</code></td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
```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' │ | ||
│ │ │ } │ | ||
│ │ │ } │ | ||
│ │ │ } │ | ||
│ │ │ │ | ||
└────────────────────────────────┴─────────────────────────────┴────────────────────────────────┘ | ||
``` | ||
### Type Modifiers | ||
<a name="Options"></a> | ||
The following are object property modifiers. Note that `Type.Optional(...)` will make the schema object property optional. `Type.Readonly(...)` however has no effect on the underlying schema as is only meaningful to TypeScript. | ||
### Options | ||
<table> | ||
<thead> | ||
<tr> | ||
<th>Type</th> | ||
<th>TypeBox</th> | ||
<th>TypeScript</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
<tr> | ||
<td>Readonly</td> | ||
<td><code>const T = Type.Object({ email: Type.Readonly(Type.String()) })</code></td> | ||
<td><code>type T = { readonly email: string }</code></td> | ||
</tr> | ||
<tr> | ||
<td>Optional</td> | ||
<td><code>const T = Type.Object({ email: Type.Optional(Type.String()) })</code></td> | ||
<td><code>type T = { email?: string }</code></td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
You can pass additional JSON schema properties on the last argument of any given type. The following are some examples. | ||
### Enums | ||
```typescript | ||
// string must be an email | ||
const T = Type.String({ format: 'email' }) | ||
It is possible to define TypeScript enums and use them as part of your TypeBox schema. | ||
Both number and string-valued enums are supported. | ||
// number must be a multiple of 2 | ||
const T = Type.Number({ multipleOf: 2 }) | ||
```ts | ||
enum Color { | ||
Red = 'red', | ||
Blue = 'blue' | ||
} | ||
const T = Type.Enum(Color); // -> json-schema: `{ enum: ['red','green'] }` | ||
// array must have at least 5 integer values | ||
const T = Type.Array(Type.Integer(), { minItems: 5 }) | ||
``` | ||
Note that the generated json-schema will only permit the *values* of the enum, not its *keys*. | ||
In TypeScript, if you omit the *value* for an enum option, TypeScript will implicitly assign the option a numeric value. | ||
E.g.: | ||
```ts | ||
enum Color { | ||
Red, // implicitly gets value `0` | ||
Blue // implicitly gets value `1` | ||
} | ||
<a name="Extended-Types"></a> | ||
const T = Type.Enum(Color); // -> json-schema: `{ enum: [0, 1] }` | ||
``` | ||
### Extended Types | ||
### User Defined Schema Properties | ||
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. | ||
It's possible to specify custom properties on schemas. The last parameter on each TypeBox function accepts an optional `UserDefinedOptions` object. Properties specified in this object will appear as properties on the resulting schema object. Consider the following. | ||
```typescript | ||
const T = Type.Object({ | ||
value: Type.String({ | ||
description: 'A required string.' | ||
}) | ||
}, { | ||
description: 'An object with a value' | ||
}) | ||
┌────────────────────────────────┬─────────────────────────────┬────────────────────────────────┐ | ||
│ TypeBox │ TypeScript │ Extended Schema │ | ||
│ │ │ │ | ||
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ | ||
│ const T = Type.Constructor([ │ type T = new ( │ const T = { │ | ||
| Type.String(), │ arg0: string, │ type: 'constructor' │ | ||
│ Type.Number(), │ arg1: number │ arguments: [{ │ | ||
│ ], Type.Boolean()) │ ) => boolean │ type: 'string' │ | ||
│ │ │ }, { │ | ||
│ │ │ type: 'number' │ | ||
│ │ │ }], │ | ||
│ │ │ returns: { │ | ||
│ │ │ type: 'boolean' │ | ||
│ │ │ } │ | ||
│ │ │ } │ | ||
│ │ │ │ | ||
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ | ||
│ const T = Type.Function([ │ type T = ( │ const T = { │ | ||
| Type.String(), │ arg0: string, │ type : 'function', │ | ||
│ Type.Number(), │ arg1: number │ arguments: [{ │ | ||
│ ], Type.Boolean()) │ ) => boolean │ type: 'string' │ | ||
│ │ │ }, { │ | ||
│ │ │ type: 'number' │ | ||
│ │ │ }], │ | ||
│ │ │ returns: { │ | ||
│ │ │ type: 'boolean' │ | ||
│ │ │ } │ | ||
│ │ │ } │ | ||
│ │ │ │ | ||
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ | ||
│ 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: 'undefined' │ | ||
| │ │ } │ | ||
│ │ │ │ | ||
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ | ||
│ const T = Type.Void() │ type T = void │ const T = { │ | ||
| │ │ type: 'void' │ | ||
| │ │ } │ | ||
│ │ │ │ | ||
└────────────────────────────────┴─────────────────────────────┴────────────────────────────────┘ | ||
``` | ||
```json | ||
{ | ||
"description": "An object with a value", | ||
"type": "object", | ||
"properties": { | ||
"value": { | ||
"description": "A required string.", | ||
"type": "string" | ||
} | ||
}, | ||
"required": [ | ||
"value" | ||
] | ||
} | ||
``` | ||
<a name="Contracts"></a> | ||
<a name="Reference-Types"></a> | ||
## Function Types | ||
### Reference Types | ||
TypeBox allows function signatures to be composed in a similar way to other types, but uses a custom schema format to achieve this. Note, this format is not JSON Schema, rather it embeds JSON Schema to encode function `arguments` and `return` types. The format also provides additional types not present in JSON Schema; `Type.Constructor()`, `Type.Void()`, `Type.Undefined()`, and `Type.Promise()`. | ||
Type referencing can be useful to help reduce schema duplication when composing complex schemas. TypeBox allows for type referencing with the `Type.Box(...)` and `Type.Ref(...)` functions. The `Type.Box(...)` function creates a container for set of common related types and the `Type.Ref(...)` function allows referencing into the box. The following shows a set of common math types contained within a box, and a vertex structure that references those types. | ||
For more information on using functions, see the [Functions](#Functions) and [Generics](#Generics) sections below. | ||
### Format | ||
The following is an example of how TypeBox encodes function signatures. | ||
```typescript | ||
type T = (a: string, b: number) => boolean | ||
{ | ||
"type": "function", | ||
"returns": { "type": "boolean" }, | ||
"arguments": [ | ||
{"type": "string" }, | ||
{"type": "number" }, | ||
] | ||
} | ||
const Math3D = Type.Box('math3d', { // const Math3D = { | ||
Vector4: Type.Object({ // $id: 'math3d', | ||
x: Type.Number(), // definitions: { | ||
y: Type.Number(), // Vector4: { | ||
z: Type.Number(), // type: 'object', | ||
w: Type.Number() // properties: { | ||
}), // x: { type: 'number' }, | ||
Vector3: Type.Object({ // y: { type: 'number' }, | ||
x: Type.Number(), // z: { type: 'number' }, | ||
y: Type.Number(), // w: { type: 'number' } | ||
z: Type.Number() // }, | ||
}), // required: ['x', 'y', 'z', 'w'] | ||
Vector2: Type.Object({ // }, | ||
x: Type.Number(), // Vector3: { | ||
y: Type.Number() // type: 'object', | ||
}) // properties: { | ||
}) // x: { 'type': 'number' }, | ||
// y: { 'type': 'number' }, | ||
// z: { 'type': 'number' } | ||
// }, | ||
// required: ['x', 'y', 'z'] | ||
// }, | ||
// Vector2: { | ||
// type: 'object', | ||
// properties: { | ||
// x: { 'type': 'number' }, | ||
// y: { 'type': 'number' }, | ||
// }, | ||
// required: ['x', 'y'] | ||
// } | ||
// } | ||
// } | ||
const Vertex = Type.Object({ // const Vertex = { | ||
position: Type.Ref(Math3D, 'Vector4'), // type: 'object', | ||
normal: Type.Ref(Math3D, 'Vector3'), // properties: { | ||
uv: Type.Ref(Math3D, 'Vector2') // position: { $ref: 'math3d#/definitions/Vector4' }, | ||
}) // normal: { $ref: 'math3d#/definitions/Vector3' }, | ||
// uv: { $ref: 'math3d#/definitions/Vector2' } | ||
// }, | ||
// required: ['position', 'normal', 'uv'] | ||
// } | ||
``` | ||
### TypeBox > TypeScript | ||
<a name="Strict"></a> | ||
<table> | ||
<thead> | ||
<tr> | ||
<th>Intrinsic</th> | ||
<th>TypeBox</th> | ||
<th>TypeScript</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
<tr> | ||
<td>Function</td> | ||
<td><code>const T = Type.Function([Type.String()], Type.String())</code></td> | ||
<td><code>type T = (arg0: string) => string</code></td> | ||
</tr> | ||
<tr> | ||
<td>Constructor</td> | ||
<td><code>const T = Type.Constructor([Type.String()], Type.String())</code></td> | ||
<td><code>type T = new (arg0: string) => string</code></td> | ||
</tr> | ||
<tr> | ||
<td>Promise</td> | ||
<td><code>const T = Type.Promise(Type.String())</code></td> | ||
<td><code>type T = Promise<string></code></td> | ||
</tr> | ||
<tr> | ||
<td>Undefined</td> | ||
<td><code>const T = Type.Undefined()</code></td> | ||
<td><code>type T = undefined</code></td> | ||
</tr> | ||
<tr> | ||
<td>Void</td> | ||
<td><code>const T = Type.Void()</code></td> | ||
<td><code>type T = void</code></td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
### Strict | ||
### TypeBox > JSON Function | ||
TypeBox includes the properties `kind` and `modifier` on each underlying schema. These properties are used to help TypeBox statically resolve the schemas to the appropriate TypeScript type as well as apply the appropriate modifiers to an objects properties (such as optional). 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 nessasary. | ||
<table> | ||
<thead> | ||
<tr> | ||
<th>Intrinsic</th> | ||
<th>TypeBox</th> | ||
<th>JSON Function</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
<tr> | ||
<td>Function</td> | ||
<td><code>const T = Type.Function([Type.String()], Type.Number())</code></td> | ||
<td><code>{ type: 'function', arguments: [ { type: 'string' } ], returns: { type: 'number' } }</code></td> | ||
</tr> | ||
<tr> | ||
<td>Constructor</td> | ||
<td><code>const T = Type.Constructor([Type.String()], Type.Number())</code></td> | ||
<td><code>{ type: 'constructor', arguments: [ { type: 'string' } ], returns: { type: 'number' } }</code></td> | ||
</tr> | ||
<tr> | ||
<td>Promise</td> | ||
<td><code>const T = Type.Promise(Type.String())</code></td> | ||
<td><code>{ type: 'promise', item: { type: 'string' } }</code></td> | ||
</tr> | ||
<tr> | ||
<td>Undefined</td> | ||
<td><code>const T = Type.Undefined()</code></td> | ||
<td><code>{ type: 'undefined' }</code></td> | ||
</tr> | ||
<tr> | ||
<td>Void</td> | ||
<td><code>const T = Type.Void()</code></td> | ||
<td><code>{ type: 'void' }</code></td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
```typescript | ||
const T = Type.Object({ // const T = { | ||
name: Type.Optional(Type.String()) // kind: Symbol(ObjectKind), | ||
}) // type: 'object', | ||
// properties: { | ||
// name: { | ||
// kind: Symbol(StringKind), | ||
// type: 'string', | ||
// modifier: Symbol(OptionalModifier) | ||
// } | ||
// } | ||
// } | ||
<a href='Functions'></a> | ||
const U = Type.Strict(T) // const U = { | ||
// type: 'object', | ||
// properties: { | ||
// name: { | ||
// type: 'string' | ||
// } | ||
// } | ||
// } | ||
``` | ||
## Functions | ||
<a name="Interfaces"></a> | ||
The following demonstrates creating function signatures for the following TypeScript types. | ||
### Interfaces | ||
### TypeScript | ||
It is possible to create interfaces from TypeBox types. Consider the following code that creates a `ControllerInterface` type that has a single function `createRecord(...)`. The following is how one might approach this in TypeScript. | ||
```typescript | ||
type T0 = (a0: number, a1: string) => boolean; | ||
interface CreateRecordRequest { | ||
data: string | ||
} | ||
type T1 = (a0: string, a1: () => string) => void; | ||
interface CreateRecordResponse { | ||
id: string | ||
} | ||
type T2 = (a0: string) => Promise<number>; | ||
interface ControllerInterface { | ||
createRecord(record: CreateRecordRequest): Promise<CreateRecordResponse> | ||
} | ||
type T3 = () => () => string; | ||
type T4 = new () => string | ||
class Controller implements ControllerInterface { | ||
async createRecord(record: CreateRecordRequest): Promise<CreateRecordResponse> { | ||
return { id: '1' } | ||
} | ||
} | ||
``` | ||
### TypeBox | ||
The following is the TypeBox equivalent. | ||
```typescript | ||
const T0 = Type.Function([Type.Number(), Type.String()], Type.Boolean()) | ||
import { Type, Static } from '@sinclair/typebox' | ||
const T1 = Type.Function([Type.String(), Type.Function([], Type.String())], Type.Void()) | ||
type CreateRecordRequest = Static<typeof CreateRecordRequest> | ||
const CreateRecordRequest = Type.Object({ | ||
data: Type.String() | ||
}) | ||
type CreateRecordResponse = Static<typeof CreateRecordResponse> | ||
const CreateRecordResponse = Type.Object({ | ||
id: Type.String() | ||
}) | ||
const T2 = Type.Function([Type.String()], Type.Promise(Type.Number())) | ||
type ControllerInterface = Static<typeof ControllerInterface> | ||
const ControllerInterface = Type.Object({ | ||
createRecord: Type.Function([CreateRecordRequest], Type.Promise(CreateRecordResponse)) | ||
}) | ||
const T3 = Type.Function([], Type.Function([], Type.String())) | ||
const T4 = Type.Constructor([], Type.String()) | ||
class Controller implements ControllerInterface { | ||
async createRecord(record: CreateRecordRequest): Promise<CreateRecordResponse> { | ||
return { id: '1' } | ||
} | ||
} | ||
``` | ||
<a name="Generics"></a> | ||
## Generics | ||
Generic function signatures can be composed with TypeScript functions with [Generic Constraints](https://www.typescriptlang.org/docs/handbook/generics.html#generic-constraints). | ||
### TypeScript | ||
Because TypeBox encodes the type information as JSON schema, it now becomes possible to reflect on the JSON schema to produce sharable metadata that can be used as machine readable documentation. | ||
```typescript | ||
type ToString = <T>(t: T) => string | ||
``` | ||
### TypeBox | ||
```typescript | ||
import { Type, Static, TStatic } from '@sinclair/typebox' | ||
const ToString = <G extends TStatic>(T: G) => Type.Function([T], Type.String()) | ||
console.log(JSON.stringify(ControllerInterface, null, 2)) | ||
// outputs: | ||
// | ||
// { | ||
// "type": "object", | ||
// "properties": { | ||
// "createRecord": { | ||
// "type": "function", | ||
// "arguments": [ | ||
// { | ||
// "type": "object", | ||
// "properties": { | ||
// "data": { | ||
// "type": "string" | ||
// } | ||
// }, | ||
// "required": [ | ||
// "data" | ||
// ] | ||
// } | ||
// ], | ||
// "returns": { | ||
// "type": "promise", | ||
// "item": { | ||
// "type": "object", | ||
// "properties": { | ||
// "id": { | ||
// "type": "string" | ||
// } | ||
// }, | ||
// "required": [ | ||
// "id" | ||
// ] | ||
// } | ||
// } | ||
// } | ||
// }, | ||
// "required": [ | ||
// "createRecord" | ||
// ] | ||
// } | ||
``` | ||
However, it's not possible to statically infer what type `ToString` is without first creating some specialized variant of it. The following creates a specialization called `NumberToString`. | ||
```typescript | ||
const NumberToString = ToString(Type.Number()) | ||
type X = Static<typeof NumberToString> | ||
<a name="Validation"></a> | ||
// X is (arg0: number) => string | ||
``` | ||
To take things a bit further, the following code contains some generic TypeScript REST setup with controllers that take some generic resource of type `T`. Below this we express that same setup using TypeBox. The resulting type `IRecordController` contains reflectable interface metadata about the `RecordController`. | ||
### TypeScript | ||
```typescript | ||
interface IController<T> { | ||
get (): Promise<T> | ||
post (resource: T): Promise<void> | ||
put (resource: T): Promise<void> | ||
delete (resource: T): Promise<void> | ||
} | ||
### Validation | ||
interface Record { | ||
key: string | ||
value: string | ||
} | ||
TypeBox does not provide JSON schema validation out of the box and expects users to select an appropriate JSON schema validation library for their needs. TypeBox schemas should match JSON Schema draft 6 so any library capable of draft 6 should be fine. A good library to use for validation is [Ajv](https://www.npmjs.com/package/ajv). The following example shows setting up Ajv 7 to work with TypeBox. | ||
class RecordController implements IController<Record> { | ||
async get (): Promise<Record> { throw 'not implemented' } | ||
async post (resource: Record): Promise<void> { /* */ } | ||
async put (resource: Record): Promise<void> { /* */ } | ||
async delete(resource: Record): Promise<void> { /* */ } | ||
} | ||
```bash | ||
$ npm install ajv ajv-formats --save | ||
``` | ||
### TypeBox | ||
```typescript | ||
import { Type, Static, TStatic } from '@sinclair/typebox' | ||
import { Type } from '@sinclair/typebox' | ||
import addFormats from 'ajv-formats' | ||
import Ajv from 'ajv' | ||
const IController = <G extends TStatic>(T: G) => Type.Object({ | ||
get: Type.Function([], Type.Promise(T)), | ||
post: Type.Function([T], Type.Promise(Type.Void())), | ||
put: Type.Function([T], Type.Promise(Type.Void())), | ||
delete: Type.Function([T], Type.Promise(Type.Void())), | ||
// Setup | ||
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' | ||
]).addKeyword('kind') | ||
.addKeyword('modifier') | ||
// TypeBox | ||
const User = Type.Object({ | ||
name: Type.String(), | ||
email: Type.String({ format: 'email' }) | ||
}) | ||
type Record = Static<typeof Record> | ||
const Record = Type.Object({ | ||
key: Type.String(), | ||
value: Type.String() | ||
// Validate | ||
const isValid = ajv.validate(User, { | ||
name: 'dave', | ||
email: 'dave@domain.com' | ||
}) | ||
type IRecordController = Static<typeof IRecordController> | ||
const IRecordController = IController(Record) | ||
class RecordController implements IRecordController { | ||
async get (): Promise<Record> { throw 'not implemented' } | ||
async post (resource: Record): Promise<void> { /* */ } | ||
async put (resource: Record): Promise<void> { /* */ } | ||
async delete(resource: Record): Promise<void> { /* */ } | ||
} | ||
// Reflect | ||
console.log(IRecordController) | ||
// | ||
// isValid -> true | ||
// | ||
``` | ||
<a href='Validation'></a> | ||
## Validation | ||
The following uses the library [Ajv](https://www.npmjs.com/package/ajv) to validate a type. | ||
```typescript | ||
import * Ajv from 'ajv' | ||
const ajv = new Ajv({ }) | ||
ajv.validate(Type.String(), 'hello') // true | ||
ajv.validate(Type.String(), 123) // false | ||
``` |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Empty package
Supply chain riskPackage does not contain any code. It may be removed, is name squatting, or the result of a faulty package publish.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
53848
664
14
4
0
2