@sinclair/typebox 0.23.4 → 0.24.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/readme.md CHANGED
@@ -2,11 +2,12 @@
2
2
 
3
3
  <h1>TypeBox</h1>
4
4
 
5
- <img src="https://github.com/sinclairzx81/typebox/blob/master/typebox.png?raw=true" />
6
-
7
5
  <p>JSON Schema Type Builder with Static Type Resolution for TypeScript</p>
6
+
7
+ <img src="https://github.com/sinclairzx81/typebox/blob/master/typebox.png?raw=true" />
8
8
 
9
-
9
+ <br />
10
+ <br />
10
11
 
11
12
  [![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)
12
13
 
@@ -33,7 +34,7 @@ import { Static, Type } from 'https://deno.land/x/typebox/src/typebox.ts'
33
34
  ```typescript
34
35
  import { Static, Type } from '@sinclair/typebox'
35
36
 
36
- const T = Type.String() // const T = { "type": "string" }
37
+ const T = Type.String() // const T = { type: 'string' }
37
38
 
38
39
  type T = Static<typeof T> // type T = string
39
40
  ```
@@ -42,11 +43,9 @@ type T = Static<typeof T> // type T = string
42
43
 
43
44
  ## Overview
44
45
 
45
- TypeBox is a 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 allows one to create a unified type that can be both statically asserted by the TypeScript compiler and runtime asserted using standard JSON Schema validation.
46
-
47
- 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 to validate schemas built with this library.
46
+ 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 allows one to compose unified types that can be statically asserted by the TypeScript compiler as well as runtime asserted using standard JSON Schema validation.
48
47
 
49
- Requires TypeScript 4.3.5 and above.
48
+ 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. It can be used in both TypeScript and JavaScript environments.
50
49
 
51
50
  License MIT
52
51
 
@@ -57,13 +56,16 @@ License MIT
57
56
  - [Types](#Types)
58
57
  - [Modifiers](#Modifiers)
59
58
  - [Options](#Options)
60
- - [Generic Types](#Generic-Types)
59
+ - [Extended Types](#Extended-Types)
61
60
  - [Reference Types](#Reference-Types)
62
61
  - [Recursive Types](#Recursive-Types)
63
- - [Extended Types](#Extended-Types)
62
+ - [Generic Types](#Generic-Types)
63
+ - [Unsafe Types](#Unsafe-Types)
64
+ - [Values](#Values)
64
65
  - [Strict](#Strict)
65
66
  - [Validation](#Validation)
66
- - [OpenAPI](#OpenAPI)
67
+ - [Compiler](#Compiler)
68
+ - [Contribute](#Contribute)
67
69
 
68
70
  <a name="Example"></a>
69
71
 
@@ -82,9 +84,9 @@ import { Static, Type } from '@sinclair/typebox'
82
84
  //--------------------------------------------------------------------------------------------
83
85
 
84
86
  type T = {
85
- id: string,
86
- name: string,
87
- timestamp: number
87
+ id: string,
88
+ name: string,
89
+ timestamp: number
88
90
  }
89
91
 
90
92
  //--------------------------------------------------------------------------------------------
@@ -93,25 +95,25 @@ type T = {
93
95
  //
94
96
  //--------------------------------------------------------------------------------------------
95
97
 
96
- const T = Type.Object({ // const T = {
97
- id: Type.String(), // type: 'object',
98
- name: Type.String(), // properties: {
99
- timestamp: Type.Integer() // id: {
100
- }) // type: 'string'
101
- // },
102
- // name: {
103
- // type: 'string'
104
- // },
105
- // timestamp: {
106
- // type: 'integer'
107
- // }
108
- // },
109
- // required: [
110
- // "id",
111
- // "name",
112
- // "timestamp"
113
- // ]
114
- // }
98
+ const T = Type.Object({ // const T = {
99
+ id: Type.String(), // type: 'object',
100
+ name: Type.String(), // properties: {
101
+ timestamp: Type.Integer() // id: {
102
+ }) // type: 'string'
103
+ // },
104
+ // name: {
105
+ // type: 'string'
106
+ // },
107
+ // timestamp: {
108
+ // type: 'integer'
109
+ // }
110
+ // },
111
+ // required: [
112
+ // "id",
113
+ // "name",
114
+ // "timestamp"
115
+ // ]
116
+ // }
115
117
 
116
118
  //--------------------------------------------------------------------------------------------
117
119
  //
@@ -119,11 +121,11 @@ const T = Type.Object({ // const T = {
119
121
  //
120
122
  //--------------------------------------------------------------------------------------------
121
123
 
122
- type T = Static<typeof T> // type T = {
123
- // id: string,
124
- // name: string,
125
- // timestamp: number
126
- // }
124
+ type T = Static<typeof T> // type T = {
125
+ // id: string,
126
+ // name: string,
127
+ // timestamp: number
128
+ // }
127
129
 
128
130
  //--------------------------------------------------------------------------------------------
129
131
  //
@@ -131,12 +133,11 @@ type T = Static<typeof T> // type T = {
131
133
  //
132
134
  //--------------------------------------------------------------------------------------------
133
135
 
134
- function receive(value: T) { // ... as a Type
135
-
136
- if(JSON.validate(T, value)) { // ... as a Schema
136
+ function receive(value: T) { // ... as a Type
137
137
 
138
+ if(JSON.validate(T, value)) { // ... as a Schema
138
139
  // ok...
139
- }
140
+ }
140
141
  }
141
142
  ```
142
143
 
@@ -158,22 +159,22 @@ The following table outlines the TypeBox mappings between TypeScript and JSON sc
158
159
  │ │ │ │
159
160
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
160
161
  │ const T = Type.String() │ type T = string │ const T = { │
161
- │ │ │ type: 'string'
162
+ │ │ │ type: 'string'
162
163
  │ │ │ } │
163
164
  │ │ │ │
164
165
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
165
166
  │ const T = Type.Number() │ type T = number │ const T = { │
166
- │ │ │ type: 'number'
167
+ │ │ │ type: 'number'
167
168
  │ │ │ } │
168
169
  │ │ │ │
169
170
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
170
171
  │ const T = Type.Integer() │ type T = number │ const T = { │
171
- │ │ │ type: 'integer'
172
+ │ │ │ type: 'integer'
172
173
  │ │ │ } │
173
174
  │ │ │ │
174
175
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
175
176
  │ const T = Type.Boolean() │ type T = boolean │ const T = { │
176
- │ │ │ type: 'boolean'
177
+ │ │ │ type: 'boolean'
177
178
  │ │ │ } │
178
179
  │ │ │ │
179
180
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
@@ -195,16 +196,16 @@ The following table outlines the TypeBox mappings between TypeScript and JSON sc
195
196
  │ │ │ │
196
197
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
197
198
  │ const T = Type.Array( │ type T = number[] │ const T = { │
198
- Type.Number() │ │ type: 'array',
199
- │ ) │ │ items: {
200
- │ │ │ type: 'number'
201
- │ │ │ }
199
+ Type.Number() │ │ type: 'array',
200
+ │ ) │ │ items: {
201
+ │ │ │ type: 'number'
202
+ │ │ │ }
202
203
  │ │ │ } │
203
204
  │ │ │ │
204
205
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
205
206
  │ const T = Type.Object({ │ type T = { │ const T = { │
206
- │ x: Type.Number(), │ x: number, │ type: 'object', │
207
- │ y: Type.Number() │ y: number │ properties: { │
207
+ │ x: Type.Number(), │ x: number, │ type: 'object', │
208
+ │ y: Type.Number() │ y: number │ properties: { │
208
209
  │ }) │ } │ x: { │
209
210
  │ │ │ type: 'number' │
210
211
  │ │ │ }, │
@@ -217,19 +218,18 @@ The following table outlines the TypeBox mappings between TypeScript and JSON sc
217
218
  │ │ │ │
218
219
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
219
220
  │ const T = Type.Tuple([ │ type T = [number, number] │ const T = { │
220
- │ Type.Number(), │ │ type: 'array',
221
- │ Type.Number() │ │ items: [ │
222
- │ ]) │ │ {
223
- │ │ │ type: 'number'
224
- │ │ │ }, {
225
- │ │ │ type: 'number'
226
- │ │ │ } │
227
- │ │ │ ], │
221
+ │ Type.Number(), │ │ type: 'array',
222
+ │ Type.Number() │ │ items: [{
223
+ │ ]) │ │ type: 'number'
224
+ │ │ │ }, {
225
+ │ │ │ type: 'number'
226
+ │ │ │ }],
228
227
  │ │ │ additionalItems: false, │
229
228
  │ │ │ minItems: 2, │
230
229
  │ │ │ maxItems: 2, │
231
230
  │ │ │ } │
232
231
  │ │ │ │
232
+ │ │ │ │
233
233
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
234
234
  │ enum Foo { │ enum Foo { │ const T = { │
235
235
  │ A, │ A, │ anyOf: [{ │
@@ -243,85 +243,81 @@ The following table outlines the TypeBox mappings between TypeScript and JSON sc
243
243
  │ │ │ │
244
244
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
245
245
  │ const T = Type.KeyOf( │ type T = keyof { │ const T = { │
246
- │ Type.Object({ │ x: number, │ enum: ['x', 'y'],
247
- │ x: Type.Number(), │ y: number │ type: 'string'
248
- │ y: Type.Number() │ } │ }
249
- │ }) │ │
250
- │ ) │ │
246
+ │ Type.Object({ │ x: number, │ anyOf: [{
247
+ │ x: Type.Number(), │ y: number │ type: 'string',
248
+ │ y: Type.Number() │ } │ const: 'x'
249
+ │ }) │ │ }, {
250
+ │ ) │ │ type: 'string',
251
+ │ │ │ const: 'y', │
252
+ │ │ │ }] │
253
+ │ │ │ } │
251
254
  │ │ │ │
252
255
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
253
256
  │ const T = Type.Union([ │ type T = string | number │ const T = { │
254
- │ Type.String(), │ │ anyOf: [{
255
- │ Type.Number() │ │ type: 'string'
256
- │ ]) │ │ }, {
257
- │ │ │ type: 'number'
258
- │ │ │ }]
257
+ │ Type.String(), │ │ anyOf: [{
258
+ │ Type.Number() │ │ type: 'string'
259
+ │ ]) │ │ }, {
260
+ │ │ │ type: 'number'
261
+ │ │ │ }]
259
262
  │ │ │ } │
260
263
  │ │ │ │
261
264
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
262
265
  │ const T = Type.Intersect([ │ type T = { │ const T = { │
263
- Type.Object({ x: number allOf: [{
264
- x: Type.Number() │ } & { │ type: 'object',
265
- }), y: number properties: {
266
- Type.Object({ │ } │ x: {
267
- y: Type.Number() │ │ type: 'number'
268
- │ }) │ │ }
269
- }) │ │ },
270
- │ │ │ required: ['x']
271
- │ │ │ }, {
272
- │ │ │ type: 'object',
273
- │ │ │ properties: { │
274
- │ │ │ y: { │
275
- │ │ │ type: 'number' │
276
- │ │ │ } │
277
- │ │ │ }, │
278
- │ │ │ required: ['y'] │
279
- │ │ │ }] │
266
+ Type.Object({ x: number type: 'object',
267
+ x: Type.Number() │ } & { │ properties: {
268
+ }), y: number x: {
269
+ Type.Object({ │ } │ type: 'number'
270
+ y: Type.Number() │ │ },
271
+ │ }) │ │ y: {
272
+ ]) │ │ type: 'number'
273
+ │ │ │ }
274
+ │ │ │ },
275
+ │ │ │ required: ['x', 'y']
280
276
  │ │ │ } │
281
277
  │ │ │ │
282
278
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
283
- │ const T = Type.Record( │ type T = { │ const T = { │
284
- Type.String(), [key: string]: number type: 'object',
285
- Type.Number() } patternProperties: {
286
- │ ) │ '^.*$': {
287
- │ │ │ type: 'number'
288
- │ │ │ }
289
- │ │ │ }
279
+ │ const T = Type.Record( │ type T = Record< │ const T = { │
280
+ Type.String(), string, type: 'object',
281
+ Type.Number() number, patternProperties: {
282
+ │ ) │ > '^.*$': {
283
+ │ │ │ type: 'number'
284
+ │ │ │ }
285
+ │ │ │ }
290
286
  │ │ │ } │
291
287
  │ │ │ │
292
288
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
293
289
  │ const T = Type.Partial( │ type T = Partial<{ │ const T = { │
294
- Type.Object({ x: number, │ type: 'object', │
295
- x: Type.Number(), y: number │ properties: { │
296
- y: Type.Number() | }> │ x: { │
297
- }) │ │ type: 'number'
290
+ Type.Object({ x: number, │ type: 'object', │
291
+ x: Type.Number(), y: number │ properties: { │
292
+ y: Type.Number() | }> │ x: { │
293
+ }) │ │ type: 'number'
298
294
  │ ) │ │ }, │
299
295
  │ │ │ y: { │
300
- │ │ │ type: 'number'
296
+ │ │ │ type: 'number'
301
297
  │ │ │ } │
302
298
  │ │ │ } │
303
299
  │ │ │ } │
304
300
  │ │ │ │
305
301
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
306
302
  │ const T = Type.Required( │ type T = Required<{ │ const T = { │
307
- Type.Object({ x?: number, │ type: 'object', │
308
- x: Type.Optional( y?: number │ properties: { │
309
- Type.Number() | }> │ x: { │
310
- ), │ │ type: 'number'
311
- y: Type.Optional( │ │ }, │
312
- Type.Number() │ │ y: { │
313
- ) │ │ type: 'number'
314
- }) │ │ } │
303
+ Type.Object({ x?: number, │ type: 'object', │
304
+ x: Type.Optional( y?: number │ properties: { │
305
+ Type.Number() | }> │ x: { │
306
+ ), │ │ type: 'number'
307
+ y: Type.Optional( │ │ }, │
308
+ Type.Number() │ │ y: { │
309
+ ) │ │ type: 'number'
310
+ }) │ │ } │
315
311
  │ ) │ │ }, │
316
312
  │ │ │ required: ['x', 'y'] │
317
313
  │ │ │ } │
318
314
  │ │ │ │
319
315
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
320
316
  │ const T = Type.Pick( │ type T = Pick<{ │ const T = { │
321
- Type.Object({ x: number, │ type: 'object', │
322
- x: Type.Number(), y: number │ properties: { │
323
- y: Type.Number(), | }, 'x'> │ x: { │
324
- }), ['x'] │ │ type: 'number'
317
+ Type.Object({ x: number, │ type: 'object', │
318
+ x: Type.Number(), y: number │ properties: { │
319
+ y: Type.Number(), | }, 'x'> │ x: { │
320
+ }), ['x'] │ │ type: 'number'
325
321
  │ ) │ │ } │
326
322
  │ │ │ }, │
327
323
  │ │ │ required: ['x'] │
@@ -329,10 +325,10 @@ The following table outlines the TypeBox mappings between TypeScript and JSON sc
329
325
  │ │ │ │
330
326
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
331
327
  │ const T = Type.Omit( │ type T = Omit<{ │ const T = { │
332
- Type.Object({ x: number, │ type: 'object', │
333
- x: Type.Number(), y: number │ properties: { │
334
- y: Type.Number(), | }, 'x'> │ y: { │
335
- }), ['x'] │ │ type: 'number'
328
+ Type.Object({ x: number, │ type: 'object', │
329
+ x: Type.Number(), y: number │ properties: { │
330
+ y: Type.Number(), | }, 'x'> │ y: { │
331
+ }), ['x'] │ │ type: 'number'
336
332
  │ ) │ │ } │
337
333
  │ │ │ }, │
338
334
  │ │ │ required: ['y'] │
@@ -352,8 +348,8 @@ TypeBox provides modifiers that can be applied to an objects properties. This al
352
348
  │ │ │ │
353
349
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
354
350
  │ const T = Type.Object({ │ type T = { │ const T = { │
355
- │ name: Type.Optional( │ name?: string, │ type: 'object', │
356
- Type.String(), │ } │ properties: { │
351
+ │ name: Type.Optional( │ name?: string, │ type: 'object', │
352
+ Type.String(), │ } │ properties: { │
357
353
  │ ) │ │ name: { │
358
354
  │ }) │ │ type: 'string' │
359
355
  │ │ │ } │
@@ -362,22 +358,22 @@ TypeBox provides modifiers that can be applied to an objects properties. This al
362
358
  │ │ │ │
363
359
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
364
360
  │ const T = Type.Object({ │ type T = { │ const T = { │
365
- │ name: Type.Readonly( │ readonly name: string, │ type: 'object', │
366
- Type.String(), │ } │ properties: { │
367
- │ ) │ │ name: {
368
- │ }) │ │ type: 'string'
369
- │ │ │ }
361
+ │ name: Type.Readonly( │ readonly name: string, │ type: 'object', │
362
+ Type.String(), │ } │ properties: { │
363
+ │ ) │ │ name: {
364
+ │ }) │ │ type: 'string'
365
+ │ │ │ }
370
366
  │ │ │ }, │
371
367
  │ │ │ required: ['name'] │
372
368
  │ │ │ } │
373
369
  │ │ │ │
374
370
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
375
371
  │ const T = Type.Object({ │ type T = { │ const T = { │
376
- │ name: Type.ReadonlyOptional( │ readonly name?: string, │ type: 'object', │
377
- Type.String(), │ } │ properties: { │
378
- │ ) │ │ name: {
379
- │ }) │ │ type: 'string'
380
- │ │ │ }
372
+ │ name: Type.ReadonlyOptional( │ readonly name?: string │ type: 'object', │
373
+ Type.String(), │ } │ properties: { │
374
+ │ ) │ │ name: {
375
+ │ }) │ │ type: 'string'
376
+ │ │ │ }
381
377
  │ │ │ } │
382
378
  │ │ │ } │
383
379
  │ │ │ │
@@ -400,142 +396,6 @@ const T = Type.Number({ multipleOf: 2 })
400
396
  // array must have at least 5 integer values
401
397
  const T = Type.Array(Type.Integer(), { minItems: 5 })
402
398
  ```
403
- <a name="Generic-Types"></a>
404
-
405
- ### Generic Types
406
-
407
- Generic types can be created using functions. The following creates a generic `Nullable<T>` type.
408
-
409
- ```typescript
410
- import { Type, Static, TSchema } from '@sinclair/typebox'
411
-
412
- // type Nullable<T> = T | null
413
-
414
- const Nullable = <T extends TSchema>(type: T) => Type.Union([type, Type.Null()])
415
-
416
- const T = Nullable(Type.String()) // const T = {
417
- // "anyOf": [{
418
- // type: 'string'
419
- // }, {
420
- // type: 'null'
421
- // }]
422
- // }
423
-
424
- type T = Static<typeof T> // type T = string | null
425
-
426
- const U = Nullable(Type.Number()) // const U = {
427
- // "anyOf": [{
428
- // type: 'number'
429
- // }, {
430
- // type: 'null'
431
- // }]
432
- // }
433
-
434
- type U = Static<typeof U> // type U = number | null
435
- ```
436
-
437
- <a name="Reference-Types"></a>
438
-
439
- ### Reference Types
440
-
441
- Types can be referenced with `Type.Ref(...)`. To reference a type, the target type must specify an `$id`.
442
-
443
- ```typescript
444
- const T = Type.String({ $id: 'T' }) // const T = {
445
- // $id: 'T',
446
- // type: 'string'
447
- // }
448
-
449
- const R = Type.Ref(T) // const R = {
450
- // $ref: 'T'
451
- // }
452
- ```
453
-
454
- It can sometimes be helpful to organize shared referenced types under a common namespace. The `Type.Namespace(...)` function can be used to create a shared definition container for related types. The following creates a `Math3D` container and a `Vertex` structure that references types in the container.
455
-
456
- ```typescript
457
- const Math3D = Type.Namespace({ // const Math3D = {
458
- Vector4: Type.Object({ // $id: 'Math3D',
459
- x: Type.Number(), // $defs: {
460
- y: Type.Number(), // Vector4: {
461
- z: Type.Number(), // type: 'object',
462
- w: Type.Number() // properties: {
463
- }), // x: { type: 'number' },
464
- Vector3: Type.Object({ // y: { type: 'number' },
465
- x: Type.Number(), // z: { type: 'number' },
466
- y: Type.Number(), // w: { type: 'number' }
467
- z: Type.Number() // },
468
- }), // required: ['x', 'y', 'z', 'w']
469
- Vector2: Type.Object({ // },
470
- x: Type.Number(), // Vector3: {
471
- y: Type.Number() // type: 'object',
472
- }) // properties: {
473
- }, { $id: 'Math3D' }) // x: { 'type': 'number' },
474
- // y: { 'type': 'number' },
475
- // z: { 'type': 'number' }
476
- // },
477
- // required: ['x', 'y', 'z']
478
- // },
479
- // Vector2: {
480
- // type: 'object',
481
- // properties: {
482
- // x: { 'type': 'number' },
483
- // y: { 'type': 'number' },
484
- // },
485
- // required: ['x', 'y']
486
- // }
487
- // }
488
- // }
489
-
490
- const Vertex = Type.Object({ // const Vertex = {
491
- position: Type.Ref(Math3D, 'Vector4'), // type: 'object',
492
- normal: Type.Ref(Math3D, 'Vector3'), // properties: {
493
- uv: Type.Ref(Math3D, 'Vector2') // position: { $ref: 'Math3D#/$defs/Vector4' },
494
- }) // normal: { $ref: 'Math3D#/$defs/Vector3' },
495
- // uv: { $ref: 'Math3D#/$defs/Vector2' }
496
- // },
497
- // required: ['position', 'normal', 'uv']
498
- // }
499
- ```
500
-
501
- <a name="Recursive-Types"></a>
502
-
503
- ### Recursive Types
504
-
505
- Recursive types can be created with the `Type.Rec(...)` function. The following creates a `Node` type that contains an array of inner Nodes. Note that due to current restrictions on TypeScript inference, it is not possible for TypeBox to statically infer for recursive types. TypeBox will infer the inner recursive type as `any`.
506
-
507
- ```typescript
508
- const Node = Type.Rec(Self => Type.Object({ // const Node = {
509
- id: Type.String(), // $id: 'Node',
510
- nodes: Type.Array(Self), // $ref: 'Node#/$defs/self',
511
- }), { $id: 'Node' }) // $defs: {
512
- // self: {
513
- // type: 'object',
514
- // properties: {
515
- // id: {
516
- // type: 'string'
517
- // },
518
- // nodes: {
519
- // type: 'array',
520
- // items: {
521
- // $ref: 'Node#/$defs/self'
522
- // }
523
- // }
524
- // }
525
- // }
526
- // }
527
-
528
- type Node = Static<typeof Node> // type Node = {
529
- // id: string
530
- // nodes: any[]
531
- // }
532
-
533
- function visit(node: Node) {
534
- for(const inner of node.nodes) {
535
- visit(inner as Node) // Assert inner as Node
536
- }
537
- }
538
- ```
539
399
 
540
400
  <a name="Extended-Types"></a>
541
401
 
@@ -549,35 +409,41 @@ In addition to JSON schema types, TypeBox provides several extended types that a
549
409
  │ │ │ │
550
410
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
551
411
  │ const T = Type.Constructor([ │ type T = new ( │ const T = { │
552
- Type.String(), │ arg0: string, │ type: 'constructor' │
553
- Type.Number(), │ arg1: number │ arguments: [{ │
554
- │ ], Type.Boolean()) │ ) => boolean │ type: 'string'
412
+ Type.String(), │ arg0: string, │ type: 'constructor' │
413
+ Type.Number() │ arg1: number │ arguments: [{ │
414
+ │ ], Type.Boolean()) │ ) => boolean │ type: 'string'
555
415
  │ │ │ }, { │
556
- │ │ │ type: 'number'
416
+ │ │ │ type: 'number'
557
417
  │ │ │ }], │
558
418
  │ │ │ returns: { │
559
- │ │ │ type: 'boolean'
419
+ │ │ │ type: 'boolean'
560
420
  │ │ │ } │
561
421
  │ │ │ } │
562
422
  │ │ │ │
563
423
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
564
424
  │ const T = Type.Function([ │ type T = ( │ const T = { │
565
- | Type.String(), │ arg0: string, │ type : 'function', │
566
- Type.Number(), │ arg1: number │ arguments: [{ │
567
- │ ], Type.Boolean()) │ ) => boolean │ type: 'string'
425
+ | Type.String(), │ arg0: string, │ type : 'function', │
426
+ Type.Number() │ arg1: number │ arguments: [{ │
427
+ │ ], Type.Boolean()) │ ) => boolean │ type: 'string'
568
428
  │ │ │ }, { │
569
- │ │ │ type: 'number'
429
+ │ │ │ type: 'number'
570
430
  │ │ │ }], │
571
431
  │ │ │ returns: { │
572
- │ │ │ type: 'boolean'
432
+ │ │ │ type: 'boolean'
573
433
  │ │ │ } │
574
434
  │ │ │ } │
575
435
  │ │ │ │
576
436
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
437
+ │ const T = Type.Uint8Array() │ type T = Uint8Array │ const T = { │
438
+ │ │ │ type: 'Uint8Array', │
439
+ │ │ │ specialized: 'Uint8Array' │
440
+ │ │ │ } │
441
+ │ │ │ │
442
+ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
577
443
  │ const T = Type.Promise( │ type T = Promise<string> │ const T = { │
578
- Type.String() │ │ type: 'promise', │
444
+ Type.String() │ │ type: 'promise', │
579
445
  │ ) │ │ item: { │
580
- │ │ │ type: 'string'
446
+ │ │ │ type: 'string'
581
447
  │ │ │ } │
582
448
  │ │ │ } │
583
449
  │ │ │ │
@@ -588,46 +454,209 @@ In addition to JSON schema types, TypeBox provides several extended types that a
588
454
  │ │ │ │
589
455
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
590
456
  │ const T = Type.Void() │ type T = void │ const T = { │
591
- │ │ │ type: 'void' │
457
+ │ │ │ type: 'null' │
592
458
  │ │ │ } │
593
459
  │ │ │ │
594
460
  └────────────────────────────────┴─────────────────────────────┴────────────────────────────────┘
595
461
  ```
596
462
 
463
+ <a name="Reference-Types"></a>
464
+
465
+ ### Reference Types
466
+
467
+ Types can be referenced with `Type.Ref(...)`. To reference a type, the target type must specify an `$id`.
468
+
469
+ ```typescript
470
+ const T = Type.String({ $id: 'T' }) // const T = {
471
+ // $id: 'T',
472
+ // type: 'string'
473
+ // }
474
+
475
+ const R = Type.Ref(T) // const R = {
476
+ // $ref: 'T'
477
+ // }
478
+ ```
479
+
480
+ <a name="Recursive-Types"></a>
481
+
482
+ ### Recursive Types
483
+
484
+ Recursive types can be created with the `Type.Recursive(...)` function.
485
+
486
+ ```typescript
487
+ const Node = Type.Recursive(Node => Type.Object({ // const Node = {
488
+ id: Type.String(), // $id: "Node",
489
+ nodes: Type.Array(Node), // type: "object",
490
+ }), { $id: 'Node' }) // properties: {
491
+ // id: {
492
+ // "type": "string"
493
+ // },
494
+ // nodes: {
495
+ // type: "array",
496
+ // items: {
497
+ // $ref: "Node"
498
+ // }
499
+ // }
500
+ // },
501
+ // required: [
502
+ // "id",
503
+ // "nodes"
504
+ // ]
505
+ // }
506
+
507
+ type Node = Static<typeof Node> // type Node = {
508
+ // id: string
509
+ // nodes: ...
510
+ // }
511
+
512
+ function visit(node: Node) {
513
+ for(const inner of node.nodes) {
514
+ visit(inner)
515
+ }
516
+ }
517
+ ```
518
+
519
+ <a name="Generic-Types"></a>
520
+
521
+ ### Generic Types
522
+
523
+ Generic types can be created using functions. The following creates a generic `Nullable<T>` type.
524
+
525
+ ```typescript
526
+ import { Type, Static, TSchema } from '@sinclair/typebox'
527
+
528
+ const Nullable = <T extends TSchema>(type: T) => Type.Union([type, Type.Null()])
529
+
530
+ const T = Nullable(Type.String()) // const T = {
531
+ // anyOf: [{
532
+ // type: 'string'
533
+ // }, {
534
+ // type: 'null'
535
+ // }]
536
+ // }
537
+
538
+ type T = Static<typeof T> // type T = string | null
539
+
540
+ const U = Nullable(Type.Number()) // const U = {
541
+ // anyOf: [{
542
+ // type: 'number'
543
+ // }, {
544
+ // type: 'null'
545
+ // }]
546
+ // }
547
+
548
+ type U = Static<typeof U> // type U = number | null
549
+ ```
550
+
551
+ <a name="Unsafe-Types"></a>
552
+
553
+ ### Unsafe Types
554
+
555
+ In some cases, you may need schema definitions that are not provided by TypeBox. In these scenarios, it's common to want to define your own schema and static type inference rules. The `Type.Unsafe(...)` function provides this functionality, allowing you to specify both schema representation and a static type to infer. Consider the following which defines a `number` schema, but will infer as a `string`.
556
+
557
+ ```typescript
558
+ const T = Type.Unsafe<string>({ type: 'number' }) // const T = {
559
+ // type: 'number'
560
+ // }
561
+
562
+ type T = Static<typeof T> // type T = string
563
+ ```
564
+
565
+ The `Type.Unsafe(...)` function can be used with function generics to create custom schema representations for validators requiring specific schema representations. An example of which would be OpenAPI's `nullable` and `string-enum` representations which are not provided by TypeBox by default. The following demonstrates creating these schemas using the `Type.Unsafe(...)` function.
566
+
567
+ ```typescript
568
+ import { Type, Static, TSchema } from '@sinclair/typebox'
569
+
570
+ //--------------------------------------------------------------------------------------------
571
+ //
572
+ // Nullable<T>
573
+ //
574
+ //--------------------------------------------------------------------------------------------
575
+
576
+ function Nullable<T extends TSchema>(schema: T) {
577
+ return Type.Unsafe<Static<T> | null>({ ...schema, nullable: true })
578
+ }
579
+
580
+ const T = Nullable(Type.String()) // const T = {
581
+ // type: 'string',
582
+ // nullable: true
583
+ // }
584
+
585
+ type T = Static<typeof T> // type T = string | null
586
+
587
+ //--------------------------------------------------------------------------------------------
588
+ //
589
+ // StringUnion<[...]>
590
+ //
591
+ //--------------------------------------------------------------------------------------------
592
+
593
+ function StringEnum<T extends string[]>(values: [...T]) {
594
+ return Type.Unsafe<T[number]>({ enum: values })
595
+ }
596
+
597
+ const T = StringEnum(['A', 'B', 'C']) // const T = {
598
+ // enum: ['A', 'B', 'C']
599
+ // }
600
+
601
+ type T = Static<typeof T> // type T = 'A' | 'B' | 'C'
602
+ ```
603
+ <a name="Values"></a>
604
+
605
+ ### Values
606
+
607
+ TypeBox can construct default values for types. TypeBox will create reasonable defaults for any given type, or produce values based on the schemas the `default` value if specified.
608
+
609
+ ```typescript
610
+ import { Value } from '@sinclair/typebox/value'
611
+ import { Type } from '@sinclair/typebox'
612
+
613
+ const T = Type.Object({
614
+ x: Type.Number({ default: 1 }),
615
+ y: Type.Number({ default: 2 }),
616
+ z: Type.Number()
617
+ })
618
+
619
+ const V = Value.Create(T) // const V = {
620
+ // x: 1,
621
+ // y: 2,
622
+ // z: 0
623
+ // }
624
+ ```
625
+
597
626
  <a name="Strict"></a>
598
627
 
599
628
  ### Strict
600
629
 
601
- TypeBox schemas contain the properties `kind` and `modifier`. These properties are provided to enable runtime type reflection on schemas, as well as helping TypeBox apply the appropriate static type inference rules. 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.
630
+ 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.
602
631
 
603
632
  ```typescript
604
- const T = Type.Object({ // const T = {
605
- name: Type.Optional(Type.String()) // kind: Symbol(ObjectKind),
606
- }) // type: 'object',
607
- // properties: {
608
- // name: {
609
- // kind: Symbol(StringKind),
610
- // type: 'string',
611
- // modifier: Symbol(OptionalModifier)
612
- // }
613
- // }
614
- // }
615
-
616
- const U = Type.Strict(T) // const U = {
617
- // type: 'object',
618
- // properties: {
619
- // name: {
620
- // type: 'string'
621
- // }
622
- // }
623
- // }
633
+ const T = Type.Object({ // const T = {
634
+ name: Type.Optional(Type.String()) // [Kind]: 'Object',
635
+ }) // type: 'object',
636
+ // properties: {
637
+ // name: {
638
+ // [Kind]: 'String',
639
+ // type: 'string',
640
+ // [Modifier]: 'Optional'
641
+ // }
642
+ // }
643
+ // }
644
+
645
+ const U = Type.Strict(T) // const U = {
646
+ // type: 'object',
647
+ // properties: {
648
+ // name: {
649
+ // type: 'string'
650
+ // }
651
+ // }
652
+ // }
624
653
  ```
625
654
 
626
655
  <a name="Validation"></a>
627
656
 
628
657
  ### Validation
629
658
 
630
- TypeBox does not provide JSON schema validation so users will need to select an appropriate JSON Schema validator for their needs. TypeBox schemas target JSON Schema draft `2019-09` so any validator capable of draft `2019-09` 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 7 to work with TypeBox.
659
+ 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 7 to work with TypeBox.
631
660
 
632
661
  ```bash
633
662
  $ npm install ajv ajv-formats --save
@@ -636,13 +665,13 @@ $ npm install ajv ajv-formats --save
636
665
  ```typescript
637
666
  //--------------------------------------------------------------------------------------------
638
667
  //
639
- // Import the 2019 compliant validator from AJV
668
+ // Import TypeBox and AJV
640
669
  //
641
670
  //--------------------------------------------------------------------------------------------
642
671
 
643
672
  import { Type } from '@sinclair/typebox'
644
673
  import addFormats from 'ajv-formats'
645
- import Ajv from 'ajv/dist/2019'
674
+ import Ajv from 'ajv'
646
675
 
647
676
  //--------------------------------------------------------------------------------------------
648
677
  //
@@ -651,22 +680,21 @@ import Ajv from 'ajv/dist/2019'
651
680
  //--------------------------------------------------------------------------------------------
652
681
 
653
682
  const ajv = addFormats(new Ajv({}), [
654
- 'date-time',
655
- 'time',
656
- 'date',
657
- 'email',
658
- 'hostname',
659
- 'ipv4',
660
- 'ipv6',
661
- 'uri',
662
- 'uri-reference',
663
- 'uuid',
664
- 'uri-template',
665
- 'json-pointer',
666
- 'relative-json-pointer',
667
- 'regex'
668
- ]).addKeyword('kind')
669
- .addKeyword('modifier')
683
+ 'date-time',
684
+ 'time',
685
+ 'date',
686
+ 'email',
687
+ 'hostname',
688
+ 'ipv4',
689
+ 'ipv6',
690
+ 'uri',
691
+ 'uri-reference',
692
+ 'uuid',
693
+ 'uri-template',
694
+ 'json-pointer',
695
+ 'relative-json-pointer',
696
+ 'regex'
697
+ ])
670
698
 
671
699
  //--------------------------------------------------------------------------------------------
672
700
  //
@@ -674,10 +702,10 @@ const ajv = addFormats(new Ajv({}), [
674
702
  //
675
703
  //--------------------------------------------------------------------------------------------
676
704
 
677
- const User = Type.Object({
678
- userId: Type.String({ format: 'uuid' }),
679
- email: Type.String({ format: 'email' }),
680
- online: Type.Boolean(),
705
+ const Vector = Type.Object({
706
+ x: Type.Number(),
707
+ y: Type.Number(),
708
+ z: Type.Number(),
681
709
  }, { additionalProperties: false })
682
710
 
683
711
  //--------------------------------------------------------------------------------------------
@@ -686,54 +714,42 @@ const User = Type.Object({
686
714
  //
687
715
  //--------------------------------------------------------------------------------------------
688
716
 
689
- const ok = ajv.validate(User, {
690
- userId: '68b4b1d8-0db6-468d-b551-02069a692044',
691
- email: 'dave@domain.com',
692
- online: true
693
- }) // -> ok
717
+ const OK = ajv.validate(Vector, {
718
+ x: 1,
719
+ y: 2,
720
+ z: 3
721
+ }) // -> true
694
722
  ```
695
723
 
696
724
  Please refer to the official AJV [documentation](https://ajv.js.org/guide/getting-started.html) for additional information on using AJV.
697
725
 
698
- ### OpenAPI
726
+ <a name="Compiler"></a>
699
727
 
700
- TypeBox can be used to create schemas for OpenAPI, however users should be mindful of some disparities between the JSON Schema and OpenAPI for versions prior to OpenAPI 3.1. Two common instances where OpenAPI diverges is the handling nullable and string enum schemas types. The following shows how you can use TypeBox to construct these types.
728
+ ### Compiler
701
729
 
702
- ```typescript
703
- import { Type, Static, TNull, TLiteral, TUnion, TSchema } from '@sinclair/typebox'
704
-
705
- //--------------------------------------------------------------------------------------------
706
- //
707
- // Nullable<T>
708
- //
709
- //--------------------------------------------------------------------------------------------
710
-
711
- function Nullable<T extends TSchema>(schema: T): TUnion<[T, TNull]> {
712
- return { ...schema, nullable: true } as any
713
- }
714
-
715
- const T = Nullable(Type.String()) // const T = {
716
- // type: 'string',
717
- // nullable: true
718
- // }
719
-
720
- type T = Static<typeof T> // type T = string | null
730
+ TypeBox provides an optional type compiler that can be used as a runtime type checker in absense of a JSON Schema validator. Please note that this compiler is not fully JSON Schema compliant and only permits compilation of TypeBox types only (where the schema representation is known in advance). The `TypeCompiler` contains a `TypeCompiler.Compile(T)` method that returns a `TypeCheck<T>` object that can be used to test the validity of a value.
721
731
 
722
- //--------------------------------------------------------------------------------------------
723
- //
724
- // StringUnion<[...]>
725
- //
726
- //--------------------------------------------------------------------------------------------
732
+ ```typescript
733
+ import { TypeCompiler } from '@sinclair/typebox/compiler'
734
+ import { Type } from '@sinclair/typebox'
735
+
736
+ const T = Type.Object({
737
+ x: Type.Number(),
738
+ y: Type.Number(),
739
+ z: Type.Number()
740
+ })
741
+
742
+ const C = TypeCompiler.Compile(T)
743
+
744
+ const OK = C.Check({
745
+ x: 1,
746
+ y: 2,
747
+ z: 3
748
+ }) // -> true
749
+ ```
727
750
 
728
- type IntoStringUnion<T> = {[K in keyof T]: T[K] extends string ? TLiteral<T[K]>: never }
751
+ <a name="Contribute"></a>
729
752
 
730
- function StringUnion<T extends string[]>(values: [...T]): TUnion<IntoStringUnion<T>> {
731
- return { enum: values } as any
732
- }
753
+ ### Contribute
733
754
 
734
- const T = StringUnion(['A', 'B', 'C']) // const T = {
735
- // enum: ['A', 'B', 'C']
736
- // }
737
-
738
- type T = Static<typeof T> // type T = 'A' | 'B' | 'C'
739
- ```
755
+ TypeBox is open to community contribution, however please ensure you submit an open issue before submitting your pull request. The TypeBox project does preference open community discussion prior to accepting new features.