@sinclair/typebox 0.23.5 → 0.24.0

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
@@ -34,7 +34,7 @@ import { Static, Type } from 'https://deno.land/x/typebox/src/typebox.ts'
34
34
  ```typescript
35
35
  import { Static, Type } from '@sinclair/typebox'
36
36
 
37
- const T = Type.String() // const T = { "type": "string" }
37
+ const T = Type.String() // const T = { type: 'string' }
38
38
 
39
39
  type T = Static<typeof T> // type T = string
40
40
  ```
@@ -43,11 +43,9 @@ type T = Static<typeof T> // type T = string
43
43
 
44
44
  ## Overview
45
45
 
46
- 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
+ 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.
47
47
 
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. TypeBox does not provide any JSON schema validation. Please use libraries such as AJV to validate schemas built with this library.
49
-
50
- 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.
51
49
 
52
50
  License MIT
53
51
 
@@ -58,13 +56,16 @@ License MIT
58
56
  - [Types](#Types)
59
57
  - [Modifiers](#Modifiers)
60
58
  - [Options](#Options)
61
- - [Generic Types](#Generic-Types)
59
+ - [Extended Types](#Extended-Types)
62
60
  - [Reference Types](#Reference-Types)
63
61
  - [Recursive Types](#Recursive-Types)
64
- - [Extended Types](#Extended-Types)
62
+ - [Generic Types](#Generic-Types)
63
+ - [Unsafe Types](#Unsafe-Types)
64
+ - [Values](#Values)
65
65
  - [Strict](#Strict)
66
66
  - [Validation](#Validation)
67
- - [OpenAPI](#OpenAPI)
67
+ - [Compiler](#Compiler)
68
+ - [Contribute](#Contribute)
68
69
 
69
70
  <a name="Example"></a>
70
71
 
@@ -83,9 +84,9 @@ import { Static, Type } from '@sinclair/typebox'
83
84
  //--------------------------------------------------------------------------------------------
84
85
 
85
86
  type T = {
86
- id: string,
87
- name: string,
88
- timestamp: number
87
+ id: string,
88
+ name: string,
89
+ timestamp: number
89
90
  }
90
91
 
91
92
  //--------------------------------------------------------------------------------------------
@@ -94,25 +95,25 @@ type T = {
94
95
  //
95
96
  //--------------------------------------------------------------------------------------------
96
97
 
97
- const T = Type.Object({ // const T = {
98
- id: Type.String(), // type: 'object',
99
- name: Type.String(), // properties: {
100
- timestamp: Type.Integer() // id: {
101
- }) // type: 'string'
102
- // },
103
- // name: {
104
- // type: 'string'
105
- // },
106
- // timestamp: {
107
- // type: 'integer'
108
- // }
109
- // },
110
- // required: [
111
- // "id",
112
- // "name",
113
- // "timestamp"
114
- // ]
115
- // }
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
+ // }
116
117
 
117
118
  //--------------------------------------------------------------------------------------------
118
119
  //
@@ -120,11 +121,11 @@ const T = Type.Object({ // const T = {
120
121
  //
121
122
  //--------------------------------------------------------------------------------------------
122
123
 
123
- type T = Static<typeof T> // type T = {
124
- // id: string,
125
- // name: string,
126
- // timestamp: number
127
- // }
124
+ type T = Static<typeof T> // type T = {
125
+ // id: string,
126
+ // name: string,
127
+ // timestamp: number
128
+ // }
128
129
 
129
130
  //--------------------------------------------------------------------------------------------
130
131
  //
@@ -132,12 +133,11 @@ type T = Static<typeof T> // type T = {
132
133
  //
133
134
  //--------------------------------------------------------------------------------------------
134
135
 
135
- function receive(value: T) { // ... as a Type
136
-
137
- if(JSON.validate(T, value)) { // ... as a Schema
136
+ function receive(value: T) { // ... as a Type
138
137
 
138
+ if(JSON.validate(T, value)) { // ... as a Schema
139
139
  // ok...
140
- }
140
+ }
141
141
  }
142
142
  ```
143
143
 
@@ -159,22 +159,22 @@ The following table outlines the TypeBox mappings between TypeScript and JSON sc
159
159
  │ │ │ │
160
160
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
161
161
  │ const T = Type.String() │ type T = string │ const T = { │
162
- │ │ │ type: 'string'
162
+ │ │ │ type: 'string'
163
163
  │ │ │ } │
164
164
  │ │ │ │
165
165
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
166
166
  │ const T = Type.Number() │ type T = number │ const T = { │
167
- │ │ │ type: 'number'
167
+ │ │ │ type: 'number'
168
168
  │ │ │ } │
169
169
  │ │ │ │
170
170
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
171
171
  │ const T = Type.Integer() │ type T = number │ const T = { │
172
- │ │ │ type: 'integer'
172
+ │ │ │ type: 'integer'
173
173
  │ │ │ } │
174
174
  │ │ │ │
175
175
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
176
176
  │ const T = Type.Boolean() │ type T = boolean │ const T = { │
177
- │ │ │ type: 'boolean'
177
+ │ │ │ type: 'boolean'
178
178
  │ │ │ } │
179
179
  │ │ │ │
180
180
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
@@ -196,16 +196,16 @@ The following table outlines the TypeBox mappings between TypeScript and JSON sc
196
196
  │ │ │ │
197
197
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
198
198
  │ const T = Type.Array( │ type T = number[] │ const T = { │
199
- Type.Number() │ │ type: 'array',
200
- │ ) │ │ items: {
201
- │ │ │ type: 'number'
202
- │ │ │ }
199
+ Type.Number() │ │ type: 'array',
200
+ │ ) │ │ items: {
201
+ │ │ │ type: 'number'
202
+ │ │ │ }
203
203
  │ │ │ } │
204
204
  │ │ │ │
205
205
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
206
206
  │ const T = Type.Object({ │ type T = { │ const T = { │
207
- │ x: Type.Number(), │ x: number, │ type: 'object', │
208
- │ y: Type.Number() │ y: number │ properties: { │
207
+ │ x: Type.Number(), │ x: number, │ type: 'object', │
208
+ │ y: Type.Number() │ y: number │ properties: { │
209
209
  │ }) │ } │ x: { │
210
210
  │ │ │ type: 'number' │
211
211
  │ │ │ }, │
@@ -218,19 +218,18 @@ The following table outlines the TypeBox mappings between TypeScript and JSON sc
218
218
  │ │ │ │
219
219
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
220
220
  │ const T = Type.Tuple([ │ type T = [number, number] │ const T = { │
221
- │ Type.Number(), │ │ type: 'array',
222
- │ Type.Number() │ │ items: [ │
223
- │ ]) │ │ {
224
- │ │ │ type: 'number'
225
- │ │ │ }, {
226
- │ │ │ type: 'number'
227
- │ │ │ } │
228
- │ │ │ ], │
221
+ │ Type.Number(), │ │ type: 'array',
222
+ │ Type.Number() │ │ items: [{
223
+ │ ]) │ │ type: 'number'
224
+ │ │ │ }, {
225
+ │ │ │ type: 'number'
226
+ │ │ │ }],
229
227
  │ │ │ additionalItems: false, │
230
228
  │ │ │ minItems: 2, │
231
229
  │ │ │ maxItems: 2, │
232
230
  │ │ │ } │
233
231
  │ │ │ │
232
+ │ │ │ │
234
233
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
235
234
  │ enum Foo { │ enum Foo { │ const T = { │
236
235
  │ A, │ A, │ anyOf: [{ │
@@ -244,85 +243,81 @@ The following table outlines the TypeBox mappings between TypeScript and JSON sc
244
243
  │ │ │ │
245
244
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
246
245
  │ const T = Type.KeyOf( │ type T = keyof { │ const T = { │
247
- │ Type.Object({ │ x: number, │ enum: ['x', 'y'],
248
- │ x: Type.Number(), │ y: number │ type: 'string'
249
- │ y: Type.Number() │ } │ }
250
- │ }) │ │
251
- │ ) │ │
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
+ │ │ │ } │
252
254
  │ │ │ │
253
255
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
254
256
  │ const T = Type.Union([ │ type T = string | number │ const T = { │
255
- │ Type.String(), │ │ anyOf: [{
256
- │ Type.Number() │ │ type: 'string'
257
- │ ]) │ │ }, {
258
- │ │ │ type: 'number'
259
- │ │ │ }]
257
+ │ Type.String(), │ │ anyOf: [{
258
+ │ Type.Number() │ │ type: 'string'
259
+ │ ]) │ │ }, {
260
+ │ │ │ type: 'number'
261
+ │ │ │ }]
260
262
  │ │ │ } │
261
263
  │ │ │ │
262
264
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
263
265
  │ const T = Type.Intersect([ │ type T = { │ const T = { │
264
- Type.Object({ x: number allOf: [{
265
- x: Type.Number() │ } & { │ type: 'object',
266
- }), y: number properties: {
267
- Type.Object({ │ } │ x: {
268
- y: Type.Number() │ │ type: 'number'
269
- │ }) │ │ }
270
- │ ]) │ │ },
271
- │ │ │ required: ['x']
272
- │ │ │ }, {
273
- │ │ │ type: 'object',
274
- │ │ │ properties: { │
275
- │ │ │ y: { │
276
- │ │ │ type: 'number' │
277
- │ │ │ } │
278
- │ │ │ }, │
279
- │ │ │ required: ['y'] │
280
- │ │ │ }] │
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']
281
276
  │ │ │ } │
282
277
  │ │ │ │
283
278
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
284
- │ const T = Type.Record( │ type T = { │ const T = { │
285
- Type.String(), [key: string]: number type: 'object',
286
- Type.Number() } patternProperties: {
287
- │ ) │ '^.*$': {
288
- │ │ │ type: 'number'
289
- │ │ │ }
290
- │ │ │ }
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
+ │ │ │ }
291
286
  │ │ │ } │
292
287
  │ │ │ │
293
288
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
294
289
  │ const T = Type.Partial( │ type T = Partial<{ │ const T = { │
295
- Type.Object({ x: number, │ type: 'object', │
296
- x: Type.Number(), y: number │ properties: { │
297
- y: Type.Number() | }> │ x: { │
298
- }) │ │ type: 'number'
290
+ Type.Object({ x: number, │ type: 'object', │
291
+ x: Type.Number(), y: number │ properties: { │
292
+ y: Type.Number() | }> │ x: { │
293
+ }) │ │ type: 'number'
299
294
  │ ) │ │ }, │
300
295
  │ │ │ y: { │
301
- │ │ │ type: 'number'
296
+ │ │ │ type: 'number'
302
297
  │ │ │ } │
303
298
  │ │ │ } │
304
299
  │ │ │ } │
305
300
  │ │ │ │
306
301
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
307
302
  │ const T = Type.Required( │ type T = Required<{ │ const T = { │
308
- Type.Object({ x?: number, │ type: 'object', │
309
- x: Type.Optional( y?: number │ properties: { │
310
- Type.Number() | }> │ x: { │
311
- ), │ │ type: 'number'
312
- y: Type.Optional( │ │ }, │
313
- Type.Number() │ │ y: { │
314
- ) │ │ type: 'number'
315
- }) │ │ } │
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
+ }) │ │ } │
316
311
  │ ) │ │ }, │
317
312
  │ │ │ required: ['x', 'y'] │
318
313
  │ │ │ } │
319
314
  │ │ │ │
320
315
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
321
316
  │ const T = Type.Pick( │ type T = Pick<{ │ const T = { │
322
- Type.Object({ x: number, │ type: 'object', │
323
- x: Type.Number(), y: number │ properties: { │
324
- y: Type.Number(), | }, 'x'> │ x: { │
325
- }), ['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'
326
321
  │ ) │ │ } │
327
322
  │ │ │ }, │
328
323
  │ │ │ required: ['x'] │
@@ -330,10 +325,10 @@ The following table outlines the TypeBox mappings between TypeScript and JSON sc
330
325
  │ │ │ │
331
326
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
332
327
  │ const T = Type.Omit( │ type T = Omit<{ │ const T = { │
333
- Type.Object({ x: number, │ type: 'object', │
334
- x: Type.Number(), y: number │ properties: { │
335
- y: Type.Number(), | }, 'x'> │ y: { │
336
- }), ['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'
337
332
  │ ) │ │ } │
338
333
  │ │ │ }, │
339
334
  │ │ │ required: ['y'] │
@@ -353,8 +348,8 @@ TypeBox provides modifiers that can be applied to an objects properties. This al
353
348
  │ │ │ │
354
349
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
355
350
  │ const T = Type.Object({ │ type T = { │ const T = { │
356
- │ name: Type.Optional( │ name?: string, │ type: 'object', │
357
- Type.String(), │ } │ properties: { │
351
+ │ name: Type.Optional( │ name?: string, │ type: 'object', │
352
+ Type.String(), │ } │ properties: { │
358
353
  │ ) │ │ name: { │
359
354
  │ }) │ │ type: 'string' │
360
355
  │ │ │ } │
@@ -363,22 +358,22 @@ TypeBox provides modifiers that can be applied to an objects properties. This al
363
358
  │ │ │ │
364
359
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
365
360
  │ const T = Type.Object({ │ type T = { │ const T = { │
366
- │ name: Type.Readonly( │ readonly name: string, │ type: 'object', │
367
- Type.String(), │ } │ properties: { │
368
- │ ) │ │ name: {
369
- │ }) │ │ type: 'string'
370
- │ │ │ }
361
+ │ name: Type.Readonly( │ readonly name: string, │ type: 'object', │
362
+ Type.String(), │ } │ properties: { │
363
+ │ ) │ │ name: {
364
+ │ }) │ │ type: 'string'
365
+ │ │ │ }
371
366
  │ │ │ }, │
372
367
  │ │ │ required: ['name'] │
373
368
  │ │ │ } │
374
369
  │ │ │ │
375
370
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
376
371
  │ const T = Type.Object({ │ type T = { │ const T = { │
377
- │ name: Type.ReadonlyOptional( │ readonly name?: string, │ type: 'object', │
378
- Type.String(), │ } │ properties: { │
379
- │ ) │ │ name: {
380
- │ }) │ │ type: 'string'
381
- │ │ │ }
372
+ │ name: Type.ReadonlyOptional( │ readonly name?: string │ type: 'object', │
373
+ Type.String(), │ } │ properties: { │
374
+ │ ) │ │ name: {
375
+ │ }) │ │ type: 'string'
376
+ │ │ │ }
382
377
  │ │ │ } │
383
378
  │ │ │ } │
384
379
  │ │ │ │
@@ -401,142 +396,6 @@ const T = Type.Number({ multipleOf: 2 })
401
396
  // array must have at least 5 integer values
402
397
  const T = Type.Array(Type.Integer(), { minItems: 5 })
403
398
  ```
404
- <a name="Generic-Types"></a>
405
-
406
- ### Generic Types
407
-
408
- Generic types can be created using functions. The following creates a generic `Nullable<T>` type.
409
-
410
- ```typescript
411
- import { Type, Static, TSchema } from '@sinclair/typebox'
412
-
413
- // type Nullable<T> = T | null
414
-
415
- const Nullable = <T extends TSchema>(type: T) => Type.Union([type, Type.Null()])
416
-
417
- const T = Nullable(Type.String()) // const T = {
418
- // "anyOf": [{
419
- // type: 'string'
420
- // }, {
421
- // type: 'null'
422
- // }]
423
- // }
424
-
425
- type T = Static<typeof T> // type T = string | null
426
-
427
- const U = Nullable(Type.Number()) // const U = {
428
- // "anyOf": [{
429
- // type: 'number'
430
- // }, {
431
- // type: 'null'
432
- // }]
433
- // }
434
-
435
- type U = Static<typeof U> // type U = number | null
436
- ```
437
-
438
- <a name="Reference-Types"></a>
439
-
440
- ### Reference Types
441
-
442
- Types can be referenced with `Type.Ref(...)`. To reference a type, the target type must specify an `$id`.
443
-
444
- ```typescript
445
- const T = Type.String({ $id: 'T' }) // const T = {
446
- // $id: 'T',
447
- // type: 'string'
448
- // }
449
-
450
- const R = Type.Ref(T) // const R = {
451
- // $ref: 'T'
452
- // }
453
- ```
454
-
455
- 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.
456
-
457
- ```typescript
458
- const Math3D = Type.Namespace({ // const Math3D = {
459
- Vector4: Type.Object({ // $id: 'Math3D',
460
- x: Type.Number(), // $defs: {
461
- y: Type.Number(), // Vector4: {
462
- z: Type.Number(), // type: 'object',
463
- w: Type.Number() // properties: {
464
- }), // x: { type: 'number' },
465
- Vector3: Type.Object({ // y: { type: 'number' },
466
- x: Type.Number(), // z: { type: 'number' },
467
- y: Type.Number(), // w: { type: 'number' }
468
- z: Type.Number() // },
469
- }), // required: ['x', 'y', 'z', 'w']
470
- Vector2: Type.Object({ // },
471
- x: Type.Number(), // Vector3: {
472
- y: Type.Number() // type: 'object',
473
- }) // properties: {
474
- }, { $id: 'Math3D' }) // x: { 'type': 'number' },
475
- // y: { 'type': 'number' },
476
- // z: { 'type': 'number' }
477
- // },
478
- // required: ['x', 'y', 'z']
479
- // },
480
- // Vector2: {
481
- // type: 'object',
482
- // properties: {
483
- // x: { 'type': 'number' },
484
- // y: { 'type': 'number' },
485
- // },
486
- // required: ['x', 'y']
487
- // }
488
- // }
489
- // }
490
-
491
- const Vertex = Type.Object({ // const Vertex = {
492
- position: Type.Ref(Math3D, 'Vector4'), // type: 'object',
493
- normal: Type.Ref(Math3D, 'Vector3'), // properties: {
494
- uv: Type.Ref(Math3D, 'Vector2') // position: { $ref: 'Math3D#/$defs/Vector4' },
495
- }) // normal: { $ref: 'Math3D#/$defs/Vector3' },
496
- // uv: { $ref: 'Math3D#/$defs/Vector2' }
497
- // },
498
- // required: ['position', 'normal', 'uv']
499
- // }
500
- ```
501
-
502
- <a name="Recursive-Types"></a>
503
-
504
- ### Recursive Types
505
-
506
- 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`.
507
-
508
- ```typescript
509
- const Node = Type.Rec(Self => Type.Object({ // const Node = {
510
- id: Type.String(), // $id: 'Node',
511
- nodes: Type.Array(Self), // $ref: 'Node#/$defs/self',
512
- }), { $id: 'Node' }) // $defs: {
513
- // self: {
514
- // type: 'object',
515
- // properties: {
516
- // id: {
517
- // type: 'string'
518
- // },
519
- // nodes: {
520
- // type: 'array',
521
- // items: {
522
- // $ref: 'Node#/$defs/self'
523
- // }
524
- // }
525
- // }
526
- // }
527
- // }
528
-
529
- type Node = Static<typeof Node> // type Node = {
530
- // id: string
531
- // nodes: any[]
532
- // }
533
-
534
- function visit(node: Node) {
535
- for(const inner of node.nodes) {
536
- visit(inner as Node) // Assert inner as Node
537
- }
538
- }
539
- ```
540
399
 
541
400
  <a name="Extended-Types"></a>
542
401
 
@@ -550,35 +409,41 @@ In addition to JSON schema types, TypeBox provides several extended types that a
550
409
  │ │ │ │
551
410
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
552
411
  │ const T = Type.Constructor([ │ type T = new ( │ const T = { │
553
- Type.String(), │ arg0: string, │ type: 'constructor' │
554
- Type.Number(), │ arg1: number │ arguments: [{ │
555
- │ ], Type.Boolean()) │ ) => boolean │ type: 'string'
412
+ Type.String(), │ arg0: string, │ type: 'constructor' │
413
+ Type.Number() │ arg1: number │ arguments: [{ │
414
+ │ ], Type.Boolean()) │ ) => boolean │ type: 'string'
556
415
  │ │ │ }, { │
557
- │ │ │ type: 'number'
416
+ │ │ │ type: 'number'
558
417
  │ │ │ }], │
559
418
  │ │ │ returns: { │
560
- │ │ │ type: 'boolean'
419
+ │ │ │ type: 'boolean'
561
420
  │ │ │ } │
562
421
  │ │ │ } │
563
422
  │ │ │ │
564
423
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
565
424
  │ const T = Type.Function([ │ type T = ( │ const T = { │
566
- | Type.String(), │ arg0: string, │ type : 'function', │
567
- Type.Number(), │ arg1: number │ arguments: [{ │
568
- │ ], Type.Boolean()) │ ) => boolean │ type: 'string'
425
+ | Type.String(), │ arg0: string, │ type : 'function', │
426
+ Type.Number() │ arg1: number │ arguments: [{ │
427
+ │ ], Type.Boolean()) │ ) => boolean │ type: 'string'
569
428
  │ │ │ }, { │
570
- │ │ │ type: 'number'
429
+ │ │ │ type: 'number'
571
430
  │ │ │ }], │
572
431
  │ │ │ returns: { │
573
- │ │ │ type: 'boolean'
432
+ │ │ │ type: 'boolean'
574
433
  │ │ │ } │
575
434
  │ │ │ } │
576
435
  │ │ │ │
577
436
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
437
+ │ const T = Type.Uint8Array() │ type T = Uint8Array │ const T = { │
438
+ │ │ │ type: 'Uint8Array', │
439
+ │ │ │ specialized: 'Uint8Array' │
440
+ │ │ │ } │
441
+ │ │ │ │
442
+ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
578
443
  │ const T = Type.Promise( │ type T = Promise<string> │ const T = { │
579
- Type.String() │ │ type: 'promise', │
444
+ Type.String() │ │ type: 'promise', │
580
445
  │ ) │ │ item: { │
581
- │ │ │ type: 'string'
446
+ │ │ │ type: 'string'
582
447
  │ │ │ } │
583
448
  │ │ │ } │
584
449
  │ │ │ │
@@ -589,46 +454,209 @@ In addition to JSON schema types, TypeBox provides several extended types that a
589
454
  │ │ │ │
590
455
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
591
456
  │ const T = Type.Void() │ type T = void │ const T = { │
592
- │ │ │ type: 'void' │
457
+ │ │ │ type: 'null' │
593
458
  │ │ │ } │
594
459
  │ │ │ │
595
460
  └────────────────────────────────┴─────────────────────────────┴────────────────────────────────┘
596
461
  ```
597
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
+
598
626
  <a name="Strict"></a>
599
627
 
600
628
  ### Strict
601
629
 
602
- 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.
603
631
 
604
632
  ```typescript
605
- const T = Type.Object({ // const T = {
606
- name: Type.Optional(Type.String()) // kind: Symbol(ObjectKind),
607
- }) // type: 'object',
608
- // properties: {
609
- // name: {
610
- // kind: Symbol(StringKind),
611
- // type: 'string',
612
- // modifier: Symbol(OptionalModifier)
613
- // }
614
- // }
615
- // }
616
-
617
- const U = Type.Strict(T) // const U = {
618
- // type: 'object',
619
- // properties: {
620
- // name: {
621
- // type: 'string'
622
- // }
623
- // }
624
- // }
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
+ // }
625
653
  ```
626
654
 
627
655
  <a name="Validation"></a>
628
656
 
629
657
  ### Validation
630
658
 
631
- 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.
632
660
 
633
661
  ```bash
634
662
  $ npm install ajv ajv-formats --save
@@ -637,13 +665,13 @@ $ npm install ajv ajv-formats --save
637
665
  ```typescript
638
666
  //--------------------------------------------------------------------------------------------
639
667
  //
640
- // Import the 2019 compliant validator from AJV
668
+ // Import TypeBox and AJV
641
669
  //
642
670
  //--------------------------------------------------------------------------------------------
643
671
 
644
672
  import { Type } from '@sinclair/typebox'
645
673
  import addFormats from 'ajv-formats'
646
- import Ajv from 'ajv/dist/2019'
674
+ import Ajv from 'ajv'
647
675
 
648
676
  //--------------------------------------------------------------------------------------------
649
677
  //
@@ -652,22 +680,21 @@ import Ajv from 'ajv/dist/2019'
652
680
  //--------------------------------------------------------------------------------------------
653
681
 
654
682
  const ajv = addFormats(new Ajv({}), [
655
- 'date-time',
656
- 'time',
657
- 'date',
658
- 'email',
659
- 'hostname',
660
- 'ipv4',
661
- 'ipv6',
662
- 'uri',
663
- 'uri-reference',
664
- 'uuid',
665
- 'uri-template',
666
- 'json-pointer',
667
- 'relative-json-pointer',
668
- 'regex'
669
- ]).addKeyword('kind')
670
- .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
+ ])
671
698
 
672
699
  //--------------------------------------------------------------------------------------------
673
700
  //
@@ -675,10 +702,10 @@ const ajv = addFormats(new Ajv({}), [
675
702
  //
676
703
  //--------------------------------------------------------------------------------------------
677
704
 
678
- const User = Type.Object({
679
- userId: Type.String({ format: 'uuid' }),
680
- email: Type.String({ format: 'email' }),
681
- online: Type.Boolean(),
705
+ const Vector = Type.Object({
706
+ x: Type.Number(),
707
+ y: Type.Number(),
708
+ z: Type.Number(),
682
709
  }, { additionalProperties: false })
683
710
 
684
711
  //--------------------------------------------------------------------------------------------
@@ -687,54 +714,42 @@ const User = Type.Object({
687
714
  //
688
715
  //--------------------------------------------------------------------------------------------
689
716
 
690
- const ok = ajv.validate(User, {
691
- userId: '68b4b1d8-0db6-468d-b551-02069a692044',
692
- email: 'dave@domain.com',
693
- online: true
694
- }) // -> ok
717
+ const OK = ajv.validate(Vector, {
718
+ x: 1,
719
+ y: 2,
720
+ z: 3
721
+ }) // -> true
695
722
  ```
696
723
 
697
724
  Please refer to the official AJV [documentation](https://ajv.js.org/guide/getting-started.html) for additional information on using AJV.
698
725
 
699
- ### OpenAPI
726
+ <a name="Compiler"></a>
700
727
 
701
- 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
702
729
 
703
- ```typescript
704
- import { Type, Static, TNull, TLiteral, TUnion, TSchema } from '@sinclair/typebox'
705
-
706
- //--------------------------------------------------------------------------------------------
707
- //
708
- // Nullable<T>
709
- //
710
- //--------------------------------------------------------------------------------------------
711
-
712
- function Nullable<T extends TSchema>(schema: T): TUnion<[T, TNull]> {
713
- return { ...schema, nullable: true } as any
714
- }
715
-
716
- const T = Nullable(Type.String()) // const T = {
717
- // type: 'string',
718
- // nullable: true
719
- // }
720
-
721
- 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.
722
731
 
723
- //--------------------------------------------------------------------------------------------
724
- //
725
- // StringUnion<[...]>
726
- //
727
- //--------------------------------------------------------------------------------------------
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
+ ```
728
750
 
729
- type IntoStringUnion<T> = {[K in keyof T]: T[K] extends string ? TLiteral<T[K]>: never }
751
+ <a name="Contribute"></a>
730
752
 
731
- function StringUnion<T extends string[]>(values: [...T]): TUnion<IntoStringUnion<T>> {
732
- return { enum: values } as any
733
- }
753
+ ### Contribute
734
754
 
735
- const T = StringUnion(['A', 'B', 'C']) // const T = {
736
- // enum: ['A', 'B', 'C']
737
- // }
738
-
739
- type T = Static<typeof T> // type T = 'A' | 'B' | 'C'
740
- ```
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.