@sinclair/typebox 0.22.1 → 0.23.3
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/package.json +2 -2
- package/readme.md +209 -258
- package/typebox.d.ts +28 -15
- package/typebox.js +104 -67
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sinclair/typebox",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.3",
|
|
4
4
|
"description": "JSONSchema Type Builder with Static Type Resolution for TypeScript",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"json-schema",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"test": "npm run spec"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
|
-
"@sinclair/hammer": "^0.
|
|
29
|
+
"@sinclair/hammer": "^0.16.3",
|
|
30
30
|
"@types/chai": "^4.2.22",
|
|
31
31
|
"@types/mocha": "^9.0.0",
|
|
32
32
|
"@types/node": "^16.11.9",
|
package/readme.md
CHANGED
|
@@ -2,8 +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
|
+
|
|
5
7
|
<p>JSON Schema Type Builder with Static Type Resolution for TypeScript</p>
|
|
6
8
|
|
|
9
|
+
|
|
10
|
+
|
|
7
11
|
[](https://badge.fury.io/js/%40sinclair%2Ftypebox) [](https://github.com/sinclairzx81/typebox/actions)
|
|
8
12
|
|
|
9
13
|
</div>
|
|
@@ -24,7 +28,7 @@ $ npm install @sinclair/typebox --save
|
|
|
24
28
|
import { Static, Type } from 'https://deno.land/x/typebox/src/typebox.ts'
|
|
25
29
|
```
|
|
26
30
|
|
|
27
|
-
##
|
|
31
|
+
## Example
|
|
28
32
|
|
|
29
33
|
```typescript
|
|
30
34
|
import { Static, Type } from '@sinclair/typebox'
|
|
@@ -38,7 +42,7 @@ type T = Static<typeof T> // type T = string
|
|
|
38
42
|
|
|
39
43
|
## Overview
|
|
40
44
|
|
|
41
|
-
TypeBox is a library that
|
|
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.
|
|
42
46
|
|
|
43
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.
|
|
44
48
|
|
|
@@ -49,7 +53,7 @@ License MIT
|
|
|
49
53
|
## Contents
|
|
50
54
|
- [Install](#Install)
|
|
51
55
|
- [Overview](#Overview)
|
|
52
|
-
- [
|
|
56
|
+
- [Usage](#Usage)
|
|
53
57
|
- [Types](#Types)
|
|
54
58
|
- [Modifiers](#Modifiers)
|
|
55
59
|
- [Options](#Options)
|
|
@@ -63,7 +67,7 @@ License MIT
|
|
|
63
67
|
|
|
64
68
|
<a name="Example"></a>
|
|
65
69
|
|
|
66
|
-
##
|
|
70
|
+
## Usage
|
|
67
71
|
|
|
68
72
|
The following demonstrates TypeBox's general usage.
|
|
69
73
|
|
|
@@ -89,25 +93,25 @@ type T = {
|
|
|
89
93
|
//
|
|
90
94
|
//--------------------------------------------------------------------------------------------
|
|
91
95
|
|
|
92
|
-
const T = Type.Object({
|
|
93
|
-
id: Type.String(),
|
|
94
|
-
name: Type.String(),
|
|
95
|
-
timestamp: Type.Integer()
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
+
// }
|
|
111
115
|
|
|
112
116
|
//--------------------------------------------------------------------------------------------
|
|
113
117
|
//
|
|
@@ -115,11 +119,11 @@ const T = Type.Object({ // const T = {
|
|
|
115
119
|
//
|
|
116
120
|
//--------------------------------------------------------------------------------------------
|
|
117
121
|
|
|
118
|
-
type T = Static<typeof T>
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
122
|
+
type T = Static<typeof T> // type T = {
|
|
123
|
+
// id: string,
|
|
124
|
+
// name: string,
|
|
125
|
+
// timestamp: number
|
|
126
|
+
// }
|
|
123
127
|
|
|
124
128
|
//--------------------------------------------------------------------------------------------
|
|
125
129
|
//
|
|
@@ -127,9 +131,9 @@ type T = Static<typeof T> // type T = {
|
|
|
127
131
|
//
|
|
128
132
|
//--------------------------------------------------------------------------------------------
|
|
129
133
|
|
|
130
|
-
function receive(value: T) {
|
|
134
|
+
function receive(value: T) { // ... as a Type
|
|
131
135
|
|
|
132
|
-
if(JSON.validate(T, value)) {
|
|
136
|
+
if(JSON.validate(T, value)) { // ... as a Schema
|
|
133
137
|
|
|
134
138
|
// ok...
|
|
135
139
|
}
|
|
@@ -145,50 +149,50 @@ The following table outlines the TypeBox mappings between TypeScript and JSON sc
|
|
|
145
149
|
```typescript
|
|
146
150
|
┌────────────────────────────────┬─────────────────────────────┬────────────────────────────────┐
|
|
147
151
|
│ TypeBox │ TypeScript │ JSON Schema │
|
|
148
|
-
│
|
|
152
|
+
│ │ │ │
|
|
149
153
|
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
|
|
150
154
|
│ const T = Type.Any() │ type T = any │ const T = { } │
|
|
151
|
-
│
|
|
155
|
+
│ │ │ │
|
|
152
156
|
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
|
|
153
157
|
│ const T = Type.Unknown() │ type T = unknown │ const T = { } │
|
|
154
|
-
│
|
|
158
|
+
│ │ │ │
|
|
155
159
|
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
|
|
156
160
|
│ const T = Type.String() │ type T = string │ const T = { │
|
|
157
161
|
│ │ │ type: 'string' │
|
|
158
162
|
│ │ │ } │
|
|
159
|
-
│
|
|
163
|
+
│ │ │ │
|
|
160
164
|
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
|
|
161
165
|
│ const T = Type.Number() │ type T = number │ const T = { │
|
|
162
166
|
│ │ │ type: 'number' │
|
|
163
167
|
│ │ │ } │
|
|
164
|
-
│
|
|
168
|
+
│ │ │ │
|
|
165
169
|
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
|
|
166
170
|
│ const T = Type.Integer() │ type T = number │ const T = { │
|
|
167
171
|
│ │ │ type: 'integer' │
|
|
168
172
|
│ │ │ } │
|
|
169
|
-
│
|
|
173
|
+
│ │ │ │
|
|
170
174
|
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
|
|
171
175
|
│ const T = Type.Boolean() │ type T = boolean │ const T = { │
|
|
172
176
|
│ │ │ type: 'boolean' │
|
|
173
177
|
│ │ │ } │
|
|
174
|
-
│
|
|
178
|
+
│ │ │ │
|
|
175
179
|
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
|
|
176
180
|
│ const T = Type.Null() │ type T = null │ const T = { │
|
|
177
181
|
│ │ │ type: 'null' │
|
|
178
182
|
│ │ │ } │
|
|
179
|
-
│
|
|
183
|
+
│ │ │ │
|
|
180
184
|
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
|
|
181
|
-
│ const T = Type.RegEx(/foo/)
|
|
185
|
+
│ const T = Type.RegEx(/foo/) │ type T = string │ const T = { │
|
|
182
186
|
│ │ │ type: 'string', │
|
|
183
187
|
│ │ │ pattern: 'foo' │
|
|
184
188
|
│ │ │ } │
|
|
185
|
-
│
|
|
189
|
+
│ │ │ │
|
|
186
190
|
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
|
|
187
191
|
│ const T = Type.Literal(42) │ type T = 42 │ const T = { │
|
|
188
192
|
│ │ │ const: 42 │
|
|
189
193
|
│ │ │ type: 'number' │
|
|
190
194
|
│ │ │ } │
|
|
191
|
-
│
|
|
195
|
+
│ │ │ │
|
|
192
196
|
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
|
|
193
197
|
│ const T = Type.Array( │ type T = number[] │ const T = { │
|
|
194
198
|
│ Type.Number() │ │ type: 'array', │
|
|
@@ -196,36 +200,36 @@ The following table outlines the TypeBox mappings between TypeScript and JSON sc
|
|
|
196
200
|
│ │ │ type: 'number' │
|
|
197
201
|
│ │ │ } │
|
|
198
202
|
│ │ │ } │
|
|
199
|
-
│
|
|
203
|
+
│ │ │ │
|
|
200
204
|
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
|
|
201
205
|
│ const T = Type.Object({ │ type T = { │ const T = { │
|
|
202
206
|
│ x: Type.Number(), │ x: number, │ type: 'object', │
|
|
203
207
|
│ y: Type.Number() │ y: number │ properties: { │
|
|
204
208
|
│ }) │ } │ x: { │
|
|
205
209
|
│ │ │ type: 'number' │
|
|
206
|
-
│
|
|
207
|
-
│
|
|
208
|
-
│
|
|
210
|
+
│ │ │ }, │
|
|
211
|
+
│ │ │ y: { │
|
|
212
|
+
│ │ │ type: 'number' │
|
|
209
213
|
│ │ │ } │
|
|
210
|
-
│
|
|
211
|
-
│
|
|
214
|
+
│ │ │ }, │
|
|
215
|
+
│ │ │ required: ['x', 'y'] │
|
|
212
216
|
│ │ │ } │
|
|
213
|
-
│
|
|
217
|
+
│ │ │ │
|
|
214
218
|
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
|
|
215
219
|
│ const T = Type.Tuple([ │ type T = [number, number] │ const T = { │
|
|
216
220
|
│ Type.Number(), │ │ type: 'array', │
|
|
217
221
|
│ Type.Number() │ │ items: [ │
|
|
218
222
|
│ ]) │ │ { │
|
|
219
|
-
│
|
|
220
|
-
│
|
|
221
|
-
│
|
|
222
|
-
│
|
|
223
|
-
│
|
|
224
|
-
│
|
|
225
|
-
│
|
|
226
|
-
│
|
|
227
|
-
│
|
|
228
|
-
│
|
|
223
|
+
│ │ │ type: 'number' │
|
|
224
|
+
│ │ │ }, { │
|
|
225
|
+
│ │ │ type: 'number' │
|
|
226
|
+
│ │ │ } │
|
|
227
|
+
│ │ │ ], │
|
|
228
|
+
│ │ │ additionalItems: false, │
|
|
229
|
+
│ │ │ minItems: 2, │
|
|
230
|
+
│ │ │ maxItems: 2, │
|
|
231
|
+
│ │ │ } │
|
|
232
|
+
│ │ │ │
|
|
229
233
|
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
|
|
230
234
|
│ enum Foo { │ enum Foo { │ const T = { │
|
|
231
235
|
│ A, │ A, │ anyOf: [{ │
|
|
@@ -244,7 +248,7 @@ The following table outlines the TypeBox mappings between TypeScript and JSON sc
|
|
|
244
248
|
│ y: Type.Number() │ } │ } │
|
|
245
249
|
│ }) │ │ │
|
|
246
250
|
│ ) │ │ │
|
|
247
|
-
│
|
|
251
|
+
│ │ │ │
|
|
248
252
|
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
|
|
249
253
|
│ const T = Type.Union([ │ type T = string | number │ const T = { │
|
|
250
254
|
│ Type.String(), │ │ anyOf: [{ │
|
|
@@ -253,38 +257,38 @@ The following table outlines the TypeBox mappings between TypeScript and JSON sc
|
|
|
253
257
|
│ │ │ type: 'number' │
|
|
254
258
|
│ │ │ }] │
|
|
255
259
|
│ │ │ } │
|
|
256
|
-
│
|
|
260
|
+
│ │ │ │
|
|
257
261
|
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
|
|
258
262
|
│ const T = Type.Intersect([ │ type T = { │ const T = { │
|
|
259
263
|
│ Type.Object({ │ x: number │ allOf: [{ │
|
|
260
264
|
│ x: Type.Number() │ } & { │ type: 'object', │
|
|
261
265
|
│ }), │ y: number │ properties: { │
|
|
262
|
-
│ Type.Object({ │ } │
|
|
263
|
-
│ y: Type.Number() │ │ type: 'number' │
|
|
266
|
+
│ Type.Object({ │ } │ x: { │
|
|
267
|
+
│ y: Type.Number() │ │ type: 'number' │
|
|
264
268
|
│ }) │ │ } │
|
|
265
269
|
│ }) │ │ }, │
|
|
266
|
-
│ │ │ required: ['
|
|
270
|
+
│ │ │ required: ['x'] │
|
|
267
271
|
│ │ │ }, { │
|
|
268
272
|
│ │ │ type: 'object', │
|
|
269
273
|
│ │ │ properties: { │
|
|
270
|
-
│ │ │
|
|
271
|
-
│
|
|
272
|
-
│
|
|
273
|
-
│
|
|
274
|
-
│
|
|
275
|
-
│
|
|
276
|
-
│
|
|
277
|
-
│
|
|
274
|
+
│ │ │ y: { │
|
|
275
|
+
│ │ │ type: 'number' │
|
|
276
|
+
│ │ │ } │
|
|
277
|
+
│ │ │ }, │
|
|
278
|
+
│ │ │ required: ['y'] │
|
|
279
|
+
│ │ │ }] │
|
|
280
|
+
│ │ │ } │
|
|
281
|
+
│ │ │ │
|
|
278
282
|
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
|
|
279
283
|
│ const T = Type.Record( │ type T = { │ const T = { │
|
|
280
284
|
│ Type.String(), │ [key: string]: number │ type: 'object', │
|
|
281
285
|
│ Type.Number() │ } │ patternProperties: { │
|
|
282
|
-
│ )
|
|
283
|
-
│
|
|
284
|
-
│
|
|
285
|
-
│
|
|
286
|
-
│
|
|
287
|
-
│
|
|
286
|
+
│ ) │ │ '^.*$': { │
|
|
287
|
+
│ │ │ type: 'number' │
|
|
288
|
+
│ │ │ } │
|
|
289
|
+
│ │ │ } │
|
|
290
|
+
│ │ │ } │
|
|
291
|
+
│ │ │ │
|
|
288
292
|
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
|
|
289
293
|
│ const T = Type.Partial( │ type T = Partial<{ │ const T = { │
|
|
290
294
|
│ Type.Object({ │ x: number, │ type: 'object', │
|
|
@@ -297,7 +301,7 @@ The following table outlines the TypeBox mappings between TypeScript and JSON sc
|
|
|
297
301
|
│ │ │ } │
|
|
298
302
|
│ │ │ } │
|
|
299
303
|
│ │ │ } │
|
|
300
|
-
│
|
|
304
|
+
│ │ │ │
|
|
301
305
|
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
|
|
302
306
|
│ const T = Type.Required( │ type T = Required<{ │ const T = { │
|
|
303
307
|
│ Type.Object({ │ x?: number, │ type: 'object', │
|
|
@@ -311,7 +315,7 @@ The following table outlines the TypeBox mappings between TypeScript and JSON sc
|
|
|
311
315
|
│ ) │ │ }, │
|
|
312
316
|
│ │ │ required: ['x', 'y'] │
|
|
313
317
|
│ │ │ } │
|
|
314
|
-
│
|
|
318
|
+
│ │ │ │
|
|
315
319
|
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
|
|
316
320
|
│ const T = Type.Pick( │ type T = Pick<{ │ const T = { │
|
|
317
321
|
│ Type.Object({ │ x: number, │ type: 'object', │
|
|
@@ -345,38 +349,38 @@ TypeBox provides modifiers that can be applied to an objects properties. This al
|
|
|
345
349
|
```typescript
|
|
346
350
|
┌────────────────────────────────┬─────────────────────────────┬────────────────────────────────┐
|
|
347
351
|
│ TypeBox │ TypeScript │ JSON Schema │
|
|
348
|
-
│
|
|
352
|
+
│ │ │ │
|
|
349
353
|
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
|
|
350
354
|
│ const T = Type.Object({ │ type T = { │ const T = { │
|
|
351
355
|
│ name: Type.Optional( │ name?: string, │ type: 'object', │
|
|
352
356
|
│ Type.String(), │ } │ properties: { │
|
|
353
|
-
│ )
|
|
357
|
+
│ ) │ │ name: { │
|
|
354
358
|
│ }) │ │ type: 'string' │
|
|
355
|
-
│
|
|
356
|
-
│
|
|
357
|
-
│
|
|
358
|
-
│
|
|
359
|
+
│ │ │ } │
|
|
360
|
+
│ │ │ } │
|
|
361
|
+
│ │ │ } │
|
|
362
|
+
│ │ │ │
|
|
359
363
|
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
|
|
360
364
|
│ const T = Type.Object({ │ type T = { │ const T = { │
|
|
361
365
|
│ name: Type.Readonly( │ readonly name: string, │ type: 'object', │
|
|
362
366
|
│ Type.String(), │ } │ properties: { │
|
|
363
|
-
│ )
|
|
367
|
+
│ ) │ │ name: { │
|
|
364
368
|
│ }) │ │ type: 'string' │
|
|
365
|
-
│
|
|
366
|
-
│
|
|
369
|
+
│ │ │ } │
|
|
370
|
+
│ │ │ }, │
|
|
367
371
|
│ │ │ required: ['name'] │
|
|
368
|
-
│
|
|
369
|
-
│
|
|
372
|
+
│ │ │ } │
|
|
373
|
+
│ │ │ │
|
|
370
374
|
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
|
|
371
375
|
│ const T = Type.Object({ │ type T = { │ const T = { │
|
|
372
376
|
│ name: Type.ReadonlyOptional( │ readonly name?: string, │ type: 'object', │
|
|
373
377
|
│ Type.String(), │ } │ properties: { │
|
|
374
|
-
│ )
|
|
378
|
+
│ ) │ │ name: { │
|
|
375
379
|
│ }) │ │ type: 'string' │
|
|
376
|
-
│
|
|
377
|
-
│
|
|
380
|
+
│ │ │ } │
|
|
381
|
+
│ │ │ } │
|
|
378
382
|
│ │ │ } │
|
|
379
|
-
│
|
|
383
|
+
│ │ │ │
|
|
380
384
|
└────────────────────────────────┴─────────────────────────────┴────────────────────────────────┘
|
|
381
385
|
```
|
|
382
386
|
|
|
@@ -409,25 +413,25 @@ import { Type, Static, TSchema } from '@sinclair/typebox'
|
|
|
409
413
|
|
|
410
414
|
const Nullable = <T extends TSchema>(type: T) => Type.Union([type, Type.Null()])
|
|
411
415
|
|
|
412
|
-
const T = Nullable(Type.String())
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
type T = Static<typeof T>
|
|
421
|
-
|
|
422
|
-
const U = Nullable(Type.Number())
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
type U = Static<typeof U>
|
|
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
|
|
431
435
|
```
|
|
432
436
|
|
|
433
437
|
<a name="Reference-Types"></a>
|
|
@@ -437,61 +441,61 @@ type U = Static<typeof U> // type U = number | null
|
|
|
437
441
|
Types can be referenced with `Type.Ref(...)`. To reference a type, the target type must specify an `$id`.
|
|
438
442
|
|
|
439
443
|
```typescript
|
|
440
|
-
const T = Type.String({ $id: 'T' })
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
+
const T = Type.String({ $id: 'T' }) // const T = {
|
|
445
|
+
// $id: 'T',
|
|
446
|
+
// type: 'string'
|
|
447
|
+
// }
|
|
444
448
|
|
|
445
|
-
const R = Type.Ref(T)
|
|
446
|
-
|
|
447
|
-
|
|
449
|
+
const R = Type.Ref(T) // const R = {
|
|
450
|
+
// $ref: 'T'
|
|
451
|
+
// }
|
|
448
452
|
```
|
|
449
453
|
|
|
450
|
-
It can 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.
|
|
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.
|
|
451
455
|
|
|
452
456
|
```typescript
|
|
453
|
-
const Math3D = Type.Namespace({
|
|
454
|
-
Vector4: Type.Object({
|
|
455
|
-
x: Type.Number(),
|
|
456
|
-
y: Type.Number(),
|
|
457
|
-
z: Type.Number(),
|
|
458
|
-
w: Type.Number()
|
|
459
|
-
}),
|
|
460
|
-
Vector3: Type.Object({
|
|
461
|
-
x: Type.Number(),
|
|
462
|
-
y: Type.Number(),
|
|
463
|
-
z: Type.Number()
|
|
464
|
-
}),
|
|
465
|
-
Vector2: Type.Object({
|
|
466
|
-
x: Type.Number(),
|
|
467
|
-
y: Type.Number()
|
|
468
|
-
})
|
|
469
|
-
}, { $id: 'Math3D' })
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
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
|
+
// }
|
|
485
489
|
|
|
486
|
-
const Vertex = Type.Object({
|
|
487
|
-
position: Type.Ref(Math3D, 'Vector4'),
|
|
488
|
-
normal: Type.Ref(Math3D, 'Vector3'),
|
|
489
|
-
uv: Type.Ref(Math3D, 'Vector2')
|
|
490
|
-
})
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
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
|
+
// }
|
|
495
499
|
```
|
|
496
500
|
|
|
497
501
|
<a name="Recursive-Types"></a>
|
|
@@ -501,30 +505,30 @@ const Vertex = Type.Object({ // const Vertex = {
|
|
|
501
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`.
|
|
502
506
|
|
|
503
507
|
```typescript
|
|
504
|
-
const Node = Type.Rec(Self => Type.Object({
|
|
505
|
-
id: Type.String(),
|
|
506
|
-
nodes: Type.Array(Self),
|
|
507
|
-
}), { $id: 'Node' })
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
type Node = Static<typeof Node>
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
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
|
+
// }
|
|
528
532
|
|
|
529
533
|
function visit(node: Node) {
|
|
530
534
|
for(const inner of node.nodes) {
|
|
@@ -542,7 +546,7 @@ In addition to JSON schema types, TypeBox provides several extended types that a
|
|
|
542
546
|
```typescript
|
|
543
547
|
┌────────────────────────────────┬─────────────────────────────┬────────────────────────────────┐
|
|
544
548
|
│ TypeBox │ TypeScript │ Extended Schema │
|
|
545
|
-
│
|
|
549
|
+
│ │ │ │
|
|
546
550
|
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
|
|
547
551
|
│ const T = Type.Constructor([ │ type T = new ( │ const T = { │
|
|
548
552
|
│ Type.String(), │ arg0: string, │ type: 'constructor' │
|
|
@@ -555,7 +559,7 @@ In addition to JSON schema types, TypeBox provides several extended types that a
|
|
|
555
559
|
│ │ │ type: 'boolean' │
|
|
556
560
|
│ │ │ } │
|
|
557
561
|
│ │ │ } │
|
|
558
|
-
│
|
|
562
|
+
│ │ │ │
|
|
559
563
|
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
|
|
560
564
|
│ const T = Type.Function([ │ type T = ( │ const T = { │
|
|
561
565
|
| Type.String(), │ arg0: string, │ type : 'function', │
|
|
@@ -568,7 +572,7 @@ In addition to JSON schema types, TypeBox provides several extended types that a
|
|
|
568
572
|
│ │ │ type: 'boolean' │
|
|
569
573
|
│ │ │ } │
|
|
570
574
|
│ │ │ } │
|
|
571
|
-
│
|
|
575
|
+
│ │ │ │
|
|
572
576
|
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
|
|
573
577
|
│ const T = Type.Promise( │ type T = Promise<string> │ const T = { │
|
|
574
578
|
│ Type.String() │ │ type: 'promise', │
|
|
@@ -576,17 +580,17 @@ In addition to JSON schema types, TypeBox provides several extended types that a
|
|
|
576
580
|
│ │ │ type: 'string' │
|
|
577
581
|
│ │ │ } │
|
|
578
582
|
│ │ │ } │
|
|
579
|
-
│
|
|
583
|
+
│ │ │ │
|
|
580
584
|
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
|
|
581
585
|
│ const T = Type.Undefined() │ type T = undefined │ const T = { │
|
|
582
586
|
│ │ │ type: 'undefined' │
|
|
583
587
|
│ │ │ } │
|
|
584
|
-
│
|
|
588
|
+
│ │ │ │
|
|
585
589
|
├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
|
|
586
590
|
│ const T = Type.Void() │ type T = void │ const T = { │
|
|
587
591
|
│ │ │ type: 'void' │
|
|
588
592
|
│ │ │ } │
|
|
589
|
-
│
|
|
593
|
+
│ │ │ │
|
|
590
594
|
└────────────────────────────────┴─────────────────────────────┴────────────────────────────────┘
|
|
591
595
|
```
|
|
592
596
|
|
|
@@ -594,7 +598,7 @@ In addition to JSON schema types, TypeBox provides several extended types that a
|
|
|
594
598
|
|
|
595
599
|
### Strict
|
|
596
600
|
|
|
597
|
-
TypeBox
|
|
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.
|
|
598
602
|
|
|
599
603
|
```typescript
|
|
600
604
|
const T = Type.Object({ // const T = {
|
|
@@ -623,7 +627,7 @@ const U = Type.Strict(T) // const U = {
|
|
|
623
627
|
|
|
624
628
|
### Validation
|
|
625
629
|
|
|
626
|
-
TypeBox does not provide JSON schema validation
|
|
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.
|
|
627
631
|
|
|
628
632
|
```bash
|
|
629
633
|
$ npm install ajv ajv-formats --save
|
|
@@ -689,64 +693,11 @@ const ok = ajv.validate(User, {
|
|
|
689
693
|
}) // -> ok
|
|
690
694
|
```
|
|
691
695
|
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
Referenced types can be added to AJV with the `ajv.addSchema(...)` function. The following moves the `userId` and `email` property types into a `Type.Namespace(...)` and registers the box with AJV.
|
|
695
|
-
|
|
696
|
-
```typescript
|
|
697
|
-
//--------------------------------------------------------------------------------------------
|
|
698
|
-
//
|
|
699
|
-
// Shared Types
|
|
700
|
-
//
|
|
701
|
-
//--------------------------------------------------------------------------------------------
|
|
702
|
-
|
|
703
|
-
const Shared = Type.Namespace({
|
|
704
|
-
UserId: Type.String({ format: 'uuid' }),
|
|
705
|
-
Email: Type.String({ format: 'email' })
|
|
706
|
-
}, { $id: 'Shared' })
|
|
707
|
-
|
|
708
|
-
//--------------------------------------------------------------------------------------------
|
|
709
|
-
//
|
|
710
|
-
// Setup Validator and Register Shared Types
|
|
711
|
-
//
|
|
712
|
-
//--------------------------------------------------------------------------------------------
|
|
713
|
-
|
|
714
|
-
const ajv = addFormats(new Ajv({}), [...])
|
|
715
|
-
.addKeyword('kind')
|
|
716
|
-
.addKeyword('modifier')
|
|
717
|
-
.addSchema(Shared) // <-- Register Shared Types
|
|
718
|
-
|
|
719
|
-
//--------------------------------------------------------------------------------------------
|
|
720
|
-
//
|
|
721
|
-
// Create a TypeBox type
|
|
722
|
-
//
|
|
723
|
-
//--------------------------------------------------------------------------------------------
|
|
724
|
-
|
|
725
|
-
const User = Type.Object({
|
|
726
|
-
userId: Type.Ref(Shared, 'UserId'),
|
|
727
|
-
email: Type.Ref(Shared, 'Email'),
|
|
728
|
-
online: Type.Boolean()
|
|
729
|
-
}, { additionalProperties: false })
|
|
730
|
-
|
|
731
|
-
//--------------------------------------------------------------------------------------------
|
|
732
|
-
//
|
|
733
|
-
// Validate Data
|
|
734
|
-
//
|
|
735
|
-
//--------------------------------------------------------------------------------------------
|
|
736
|
-
|
|
737
|
-
const ok = ajv.validate(User, {
|
|
738
|
-
userId: '68b4b1d8-0db6-468d-b551-02069a692044',
|
|
739
|
-
email: 'dave@domain.com',
|
|
740
|
-
online: true
|
|
741
|
-
}) // -> ok
|
|
742
|
-
|
|
743
|
-
```
|
|
744
|
-
|
|
745
|
-
Please refer to the official AJV [documentation](https://ajv.js.org/guide/getting-started.html) for additional information.
|
|
696
|
+
Please refer to the official AJV [documentation](https://ajv.js.org/guide/getting-started.html) for additional information on using AJV.
|
|
746
697
|
|
|
747
698
|
### OpenAPI
|
|
748
699
|
|
|
749
|
-
TypeBox can be used to create schemas for OpenAPI, however users should be
|
|
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.
|
|
750
701
|
|
|
751
702
|
```typescript
|
|
752
703
|
import { Type, Static, TNull, TLiteral, TUnion, TSchema } from '@sinclair/typebox'
|
|
@@ -761,12 +712,12 @@ function Nullable<T extends TSchema>(schema: T): TUnion<[T, TNull]> {
|
|
|
761
712
|
return { ...schema, nullable: true } as any
|
|
762
713
|
}
|
|
763
714
|
|
|
764
|
-
const T = Nullable(Type.String())
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
715
|
+
const T = Nullable(Type.String()) // const T = {
|
|
716
|
+
// type: 'string',
|
|
717
|
+
// nullable: true
|
|
718
|
+
// }
|
|
768
719
|
|
|
769
|
-
type T = Static<typeof T>
|
|
720
|
+
type T = Static<typeof T> // type T = string | null
|
|
770
721
|
|
|
771
722
|
//--------------------------------------------------------------------------------------------
|
|
772
723
|
//
|
|
@@ -780,9 +731,9 @@ function StringUnion<T extends string[]>(values: [...T]): TUnion<IntoStringUnion
|
|
|
780
731
|
return { enum: values } as any
|
|
781
732
|
}
|
|
782
733
|
|
|
783
|
-
const T = StringUnion(['A', 'B', 'C'])
|
|
784
|
-
|
|
785
|
-
|
|
734
|
+
const T = StringUnion(['A', 'B', 'C']) // const T = {
|
|
735
|
+
// enum: ['A', 'B', 'C']
|
|
736
|
+
// }
|
|
786
737
|
|
|
787
|
-
type T = Static<typeof T>
|
|
738
|
+
type T = Static<typeof T> // type T = 'A' | 'B' | 'C'
|
|
788
739
|
```
|
package/typebox.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export declare type TOptional<T extends TSchema> = T & {
|
|
|
11
11
|
export declare type TReadonly<T extends TSchema> = T & {
|
|
12
12
|
modifier: typeof ReadonlyModifier;
|
|
13
13
|
};
|
|
14
|
-
export declare const
|
|
14
|
+
export declare const NamespaceKind: unique symbol;
|
|
15
15
|
export declare const KeyOfKind: unique symbol;
|
|
16
16
|
export declare const IntersectKind: unique symbol;
|
|
17
17
|
export declare const UnionKind: unique symbol;
|
|
@@ -28,6 +28,7 @@ export declare const BooleanKind: unique symbol;
|
|
|
28
28
|
export declare const NullKind: unique symbol;
|
|
29
29
|
export declare const UnknownKind: unique symbol;
|
|
30
30
|
export declare const AnyKind: unique symbol;
|
|
31
|
+
export declare const RefKind: unique symbol;
|
|
31
32
|
export interface CustomOptions {
|
|
32
33
|
$id?: string;
|
|
33
34
|
title?: string;
|
|
@@ -67,7 +68,7 @@ export declare type TDefinitions = {
|
|
|
67
68
|
[key: string]: TSchema;
|
|
68
69
|
};
|
|
69
70
|
export declare type TNamespace<T extends TDefinitions> = {
|
|
70
|
-
kind: typeof
|
|
71
|
+
kind: typeof NamespaceKind;
|
|
71
72
|
$defs: T;
|
|
72
73
|
} & CustomOptions;
|
|
73
74
|
export interface TSchema {
|
|
@@ -141,6 +142,11 @@ export interface TEnum<T extends TEnumKey[]> extends TSchema, CustomOptions {
|
|
|
141
142
|
kind: typeof EnumKind;
|
|
142
143
|
anyOf: T;
|
|
143
144
|
}
|
|
145
|
+
export interface TRef<T extends TSchema> extends TSchema, CustomOptions {
|
|
146
|
+
$static: Static<T>;
|
|
147
|
+
kind: typeof RefKind;
|
|
148
|
+
$ref: string;
|
|
149
|
+
}
|
|
144
150
|
export interface TString extends TSchema, StringOptions<string> {
|
|
145
151
|
$static: string;
|
|
146
152
|
kind: typeof StringKind;
|
|
@@ -209,6 +215,9 @@ export interface TVoid extends TSchema, CustomOptions {
|
|
|
209
215
|
kind: typeof VoidKind;
|
|
210
216
|
type: 'void';
|
|
211
217
|
}
|
|
218
|
+
export declare type Selectable = TObject<TProperties> | TRef<TObject<TProperties>>;
|
|
219
|
+
export declare type SelectablePropertyKeys<T extends Selectable> = T extends TObject<infer U> ? keyof U : T extends TRef<TObject<infer U>> ? keyof U : never;
|
|
220
|
+
export declare type SelectableProperties<T extends Selectable> = T extends TObject<infer U> ? U : T extends TRef<TObject<infer U>> ? U : never;
|
|
212
221
|
export declare type UnionToIntersect<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
|
|
213
222
|
export declare type StaticReadonlyOptionalPropertyKeys<T extends TProperties> = {
|
|
214
223
|
[K in keyof T]: T[K] extends TReadonlyOptional<TSchema> ? K : never;
|
|
@@ -262,6 +271,7 @@ export declare type StaticFunction<T extends readonly TSchema[], U extends TSche
|
|
|
262
271
|
export declare type StaticPromise<T extends TSchema> = Promise<Static<T>>;
|
|
263
272
|
export declare type Static<T extends TSchema> = T['$static'];
|
|
264
273
|
export declare class TypeBuilder {
|
|
274
|
+
protected readonly schemas: Map<string, TSchema>;
|
|
265
275
|
/** `Standard` Modifies an object property to be both readonly and optional */
|
|
266
276
|
ReadonlyOptional<T extends TSchema>(item: T): TReadonlyOptional<T>;
|
|
267
277
|
/** `Standard` Modifies an object property to be readonly */
|
|
@@ -298,18 +308,18 @@ export declare class TypeBuilder {
|
|
|
298
308
|
Unknown(options?: CustomOptions): TUnknown;
|
|
299
309
|
/** `Standard` Creates an any type */
|
|
300
310
|
Any(options?: CustomOptions): TAny;
|
|
301
|
-
/** `Standard` Creates a keyof type from the given object */
|
|
302
|
-
KeyOf<T extends TObject<TProperties>>(schema: T, options?: CustomOptions): TKeyOf<(keyof T['properties'])[]>;
|
|
303
311
|
/** `Standard` Creates a record type */
|
|
304
312
|
Record<K extends TRecordKey, T extends TSchema>(key: K, value: T, options?: ObjectOptions): TRecord<K, T>;
|
|
313
|
+
/** `Standard` Creates a keyof type from the given object */
|
|
314
|
+
KeyOf<T extends TObject<TProperties> | TRef<TObject<TProperties>>>(object: T, options?: CustomOptions): TKeyOf<SelectablePropertyKeys<T>[]>;
|
|
305
315
|
/** `Standard` Makes all properties in the given object type required */
|
|
306
|
-
Required<T extends TObject<
|
|
316
|
+
Required<T extends TObject<TProperties> | TRef<TObject<TProperties>>>(object: T, options?: ObjectOptions): TObject<StaticRequired<T['properties']>>;
|
|
307
317
|
/** `Standard` Makes all properties in the given object type optional */
|
|
308
|
-
Partial<T extends TObject<
|
|
318
|
+
Partial<T extends TObject<TProperties> | TRef<TObject<TProperties>>>(object: T, options?: ObjectOptions): TObject<StaticPartial<T['properties']>>;
|
|
309
319
|
/** `Standard` Picks property keys from the given object type */
|
|
310
|
-
Pick<T extends TObject<TProperties
|
|
320
|
+
Pick<T extends TObject<TProperties> | TRef<TObject<TProperties>>, K extends SelectablePropertyKeys<T>[]>(object: T, keys: [...K], options?: ObjectOptions): TObject<Pick<SelectableProperties<T>, K[number]>>;
|
|
311
321
|
/** `Standard` Omits property keys from the given object type */
|
|
312
|
-
Omit<T extends TObject<
|
|
322
|
+
Omit<T extends TObject<TProperties> | TRef<TObject<TProperties>>, K extends SelectablePropertyKeys<T>[]>(object: T, keys: [...K], options?: ObjectOptions): TObject<Omit<SelectableProperties<T>, K[number]>>;
|
|
313
323
|
/** `Standard` Omits the `kind` and `modifier` properties from the underlying schema */
|
|
314
324
|
Strict<T extends TSchema>(schema: T, options?: CustomOptions): T;
|
|
315
325
|
/** `Extended` Creates a constructor type */
|
|
@@ -322,14 +332,17 @@ export declare class TypeBuilder {
|
|
|
322
332
|
Undefined(options?: CustomOptions): TUndefined;
|
|
323
333
|
/** `Extended` Creates a void type */
|
|
324
334
|
Void(options?: CustomOptions): TVoid;
|
|
335
|
+
/** `Standard` Creates a namespace for a set of related types */
|
|
336
|
+
Namespace<T extends TDefinitions>($defs: T, options?: CustomOptions): TNamespace<T>;
|
|
337
|
+
/** `Standard` References a type within a namespace. The referenced namespace must specify an `$id` */
|
|
338
|
+
Ref<T extends TNamespace<TDefinitions>, K extends keyof T['$defs']>(namespace: T, key: K): TRef<T['$defs'][K]>;
|
|
339
|
+
/** `Standard` References type. The referenced type must specify an `$id` */
|
|
340
|
+
Ref<T extends TSchema>(schema: T): TRef<T>;
|
|
325
341
|
/** `Experimental` Creates a recursive type */
|
|
326
342
|
Rec<T extends TSchema>(callback: (self: TAny) => T, options?: CustomOptions): T;
|
|
327
|
-
/**
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
Ref<T extends TNamespace<TDefinitions>, K extends keyof T['$defs']>(box: T, key: K): T['$defs'][K];
|
|
332
|
-
/** `Experimental` References type. The referenced type must specify an `$id` */
|
|
333
|
-
Ref<T extends TSchema>(schema: T): T;
|
|
343
|
+
/** Conditionally stores and schema if it contains an $id and returns */
|
|
344
|
+
protected Store<T extends TSchema | TNamespace<TDefinitions>, S = Omit<T, '$static'>>(schema: S): T;
|
|
345
|
+
/** Conditionally dereferences a schema if RefKind. Otherwise return argument */
|
|
346
|
+
protected Deref<T extends TSchema>(schema: T): any;
|
|
334
347
|
}
|
|
335
348
|
export declare const Type: TypeBuilder;
|
package/typebox.js
CHANGED
|
@@ -27,7 +27,7 @@ THE SOFTWARE.
|
|
|
27
27
|
|
|
28
28
|
---------------------------------------------------------------------------*/
|
|
29
29
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
|
-
exports.Type = exports.TypeBuilder = exports.VoidKind = exports.UndefinedKind = exports.PromiseKind = exports.FunctionKind = exports.ConstructorKind = exports.AnyKind = exports.UnknownKind = exports.NullKind = exports.BooleanKind = exports.IntegerKind = exports.NumberKind = exports.StringKind = exports.LiteralKind = exports.EnumKind = exports.ArrayKind = exports.RecordKind = exports.ObjectKind = exports.TupleKind = exports.UnionKind = exports.IntersectKind = exports.KeyOfKind = exports.
|
|
30
|
+
exports.Type = exports.TypeBuilder = exports.VoidKind = exports.UndefinedKind = exports.PromiseKind = exports.FunctionKind = exports.ConstructorKind = exports.RefKind = exports.AnyKind = exports.UnknownKind = exports.NullKind = exports.BooleanKind = exports.IntegerKind = exports.NumberKind = exports.StringKind = exports.LiteralKind = exports.EnumKind = exports.ArrayKind = exports.RecordKind = exports.ObjectKind = exports.TupleKind = exports.UnionKind = exports.IntersectKind = exports.KeyOfKind = exports.NamespaceKind = exports.ReadonlyModifier = exports.OptionalModifier = exports.ReadonlyOptionalModifier = void 0;
|
|
31
31
|
// --------------------------------------------------------------------------
|
|
32
32
|
// Modifiers
|
|
33
33
|
// --------------------------------------------------------------------------
|
|
@@ -37,7 +37,7 @@ exports.ReadonlyModifier = Symbol('ReadonlyModifier');
|
|
|
37
37
|
// --------------------------------------------------------------------------
|
|
38
38
|
// Schema Standard
|
|
39
39
|
// --------------------------------------------------------------------------
|
|
40
|
-
exports.
|
|
40
|
+
exports.NamespaceKind = Symbol('NamespaceKind');
|
|
41
41
|
exports.KeyOfKind = Symbol('KeyOfKind');
|
|
42
42
|
exports.IntersectKind = Symbol('IntersectKind');
|
|
43
43
|
exports.UnionKind = Symbol('UnionKind');
|
|
@@ -54,6 +54,7 @@ exports.BooleanKind = Symbol('BooleanKind');
|
|
|
54
54
|
exports.NullKind = Symbol('NullKind');
|
|
55
55
|
exports.UnknownKind = Symbol('UnknownKind');
|
|
56
56
|
exports.AnyKind = Symbol('AnyKind');
|
|
57
|
+
exports.RefKind = Symbol('RefKind');
|
|
57
58
|
// --------------------------------------------------------------------------
|
|
58
59
|
// Extended Schema Types
|
|
59
60
|
// --------------------------------------------------------------------------
|
|
@@ -82,6 +83,7 @@ function clone(object) {
|
|
|
82
83
|
// TypeBuilder
|
|
83
84
|
// --------------------------------------------------------------------------
|
|
84
85
|
class TypeBuilder {
|
|
86
|
+
schemas = new Map();
|
|
85
87
|
/** `Standard` Modifies an object property to be both readonly and optional */
|
|
86
88
|
ReadonlyOptional(item) {
|
|
87
89
|
return { ...item, modifier: exports.ReadonlyOptionalModifier };
|
|
@@ -99,9 +101,10 @@ class TypeBuilder {
|
|
|
99
101
|
const additionalItems = false;
|
|
100
102
|
const minItems = items.length;
|
|
101
103
|
const maxItems = items.length;
|
|
102
|
-
|
|
104
|
+
const schema = ((items.length > 0)
|
|
103
105
|
? { ...options, kind: exports.TupleKind, type: 'array', items, additionalItems, minItems, maxItems }
|
|
104
106
|
: { ...options, kind: exports.TupleKind, type: 'array', minItems, maxItems });
|
|
107
|
+
return this.Store(schema);
|
|
105
108
|
}
|
|
106
109
|
/** `Standard` Creates an object type with the given properties */
|
|
107
110
|
Object(properties, options = {}) {
|
|
@@ -114,35 +117,35 @@ class TypeBuilder {
|
|
|
114
117
|
});
|
|
115
118
|
const required_names = property_names.filter(name => !optional.includes(name));
|
|
116
119
|
const required = (required_names.length > 0) ? required_names : undefined;
|
|
117
|
-
return ((required)
|
|
120
|
+
return this.Store(((required)
|
|
118
121
|
? { ...options, kind: exports.ObjectKind, type: 'object', properties, required }
|
|
119
|
-
: { ...options, kind: exports.ObjectKind, type: 'object', properties });
|
|
122
|
+
: { ...options, kind: exports.ObjectKind, type: 'object', properties }));
|
|
120
123
|
}
|
|
121
124
|
/** `Standard` Creates an intersect type. */
|
|
122
125
|
Intersect(items, options = {}) {
|
|
123
|
-
return { ...options, kind: exports.IntersectKind, type: 'object', allOf: items };
|
|
126
|
+
return this.Store({ ...options, kind: exports.IntersectKind, type: 'object', allOf: items });
|
|
124
127
|
}
|
|
125
128
|
/** `Standard` Creates a union type */
|
|
126
129
|
Union(items, options = {}) {
|
|
127
|
-
return { ...options, kind: exports.UnionKind, anyOf: items };
|
|
130
|
+
return this.Store({ ...options, kind: exports.UnionKind, anyOf: items });
|
|
128
131
|
}
|
|
129
132
|
/** `Standard` Creates an array type */
|
|
130
133
|
Array(items, options = {}) {
|
|
131
|
-
return { ...options, kind: exports.ArrayKind, type: 'array', items };
|
|
134
|
+
return this.Store({ ...options, kind: exports.ArrayKind, type: 'array', items });
|
|
132
135
|
}
|
|
133
136
|
/** `Standard` Creates an enum type from a TypeScript enum */
|
|
134
137
|
Enum(item, options = {}) {
|
|
135
138
|
const values = Object.keys(item).filter(key => isNaN(key)).map(key => item[key]);
|
|
136
139
|
const anyOf = values.map(value => typeof value === 'string' ? { type: 'string', const: value } : { type: 'number', const: value });
|
|
137
|
-
return { ...options, kind: exports.EnumKind, anyOf };
|
|
140
|
+
return this.Store({ ...options, kind: exports.EnumKind, anyOf });
|
|
138
141
|
}
|
|
139
142
|
/** `Standard` Creates a literal type. Supports string, number and boolean values only */
|
|
140
143
|
Literal(value, options = {}) {
|
|
141
|
-
return { ...options, kind: exports.LiteralKind, const: value, type: typeof value };
|
|
144
|
+
return this.Store({ ...options, kind: exports.LiteralKind, const: value, type: typeof value });
|
|
142
145
|
}
|
|
143
146
|
/** `Standard` Creates a string type */
|
|
144
147
|
String(options = {}) {
|
|
145
|
-
return { ...options, kind: exports.StringKind, type: 'string' };
|
|
148
|
+
return this.Store({ ...options, kind: exports.StringKind, type: 'string' });
|
|
146
149
|
}
|
|
147
150
|
/** `Standard` Creates a string type from a regular expression */
|
|
148
151
|
RegEx(regex, options = {}) {
|
|
@@ -150,32 +153,27 @@ class TypeBuilder {
|
|
|
150
153
|
}
|
|
151
154
|
/** `Standard` Creates a number type */
|
|
152
155
|
Number(options = {}) {
|
|
153
|
-
return { ...options, kind: exports.NumberKind, type: 'number' };
|
|
156
|
+
return this.Store({ ...options, kind: exports.NumberKind, type: 'number' });
|
|
154
157
|
}
|
|
155
158
|
/** `Standard` Creates an integer type */
|
|
156
159
|
Integer(options = {}) {
|
|
157
|
-
return { ...options, kind: exports.IntegerKind, type: 'integer' };
|
|
160
|
+
return this.Store({ ...options, kind: exports.IntegerKind, type: 'integer' });
|
|
158
161
|
}
|
|
159
162
|
/** `Standard` Creates a boolean type */
|
|
160
163
|
Boolean(options = {}) {
|
|
161
|
-
return { ...options, kind: exports.BooleanKind, type: 'boolean' };
|
|
164
|
+
return this.Store({ ...options, kind: exports.BooleanKind, type: 'boolean' });
|
|
162
165
|
}
|
|
163
166
|
/** `Standard` Creates a null type */
|
|
164
167
|
Null(options = {}) {
|
|
165
|
-
return { ...options, kind: exports.NullKind, type: 'null' };
|
|
168
|
+
return this.Store({ ...options, kind: exports.NullKind, type: 'null' });
|
|
166
169
|
}
|
|
167
170
|
/** `Standard` Creates an unknown type */
|
|
168
171
|
Unknown(options = {}) {
|
|
169
|
-
return { ...options, kind: exports.UnknownKind };
|
|
172
|
+
return this.Store({ ...options, kind: exports.UnknownKind });
|
|
170
173
|
}
|
|
171
174
|
/** `Standard` Creates an any type */
|
|
172
175
|
Any(options = {}) {
|
|
173
|
-
return { ...options, kind: exports.AnyKind };
|
|
174
|
-
}
|
|
175
|
-
/** `Standard` Creates a keyof type from the given object */
|
|
176
|
-
KeyOf(schema, options = {}) {
|
|
177
|
-
const keys = Object.keys(schema.properties);
|
|
178
|
-
return { ...options, kind: exports.KeyOfKind, type: 'string', enum: keys };
|
|
176
|
+
return this.Store({ ...options, kind: exports.AnyKind });
|
|
179
177
|
}
|
|
180
178
|
/** `Standard` Creates a record type */
|
|
181
179
|
Record(key, value, options = {}) {
|
|
@@ -188,14 +186,21 @@ class TypeBuilder {
|
|
|
188
186
|
default: throw Error('Invalid Record Key');
|
|
189
187
|
}
|
|
190
188
|
})();
|
|
191
|
-
return { ...options, kind: exports.RecordKind, type: 'object', patternProperties: { [pattern]: value } };
|
|
189
|
+
return this.Store({ ...options, kind: exports.RecordKind, type: 'object', patternProperties: { [pattern]: value } });
|
|
190
|
+
}
|
|
191
|
+
/** `Standard` Creates a keyof type from the given object */
|
|
192
|
+
KeyOf(object, options = {}) {
|
|
193
|
+
const source = this.Deref(object);
|
|
194
|
+
const keys = Object.keys(source.properties);
|
|
195
|
+
return this.Store({ ...options, kind: exports.KeyOfKind, type: 'string', enum: keys });
|
|
192
196
|
}
|
|
193
197
|
/** `Standard` Makes all properties in the given object type required */
|
|
194
|
-
Required(
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
198
|
+
Required(object, options = {}) {
|
|
199
|
+
const source = this.Deref(object);
|
|
200
|
+
const schema = { ...clone(source), ...options };
|
|
201
|
+
schema.required = Object.keys(schema.properties);
|
|
202
|
+
for (const key of Object.keys(schema.properties)) {
|
|
203
|
+
const property = schema.properties[key];
|
|
199
204
|
switch (property.modifier) {
|
|
200
205
|
case exports.ReadonlyOptionalModifier:
|
|
201
206
|
property.modifier = exports.ReadonlyModifier;
|
|
@@ -211,14 +216,15 @@ class TypeBuilder {
|
|
|
211
216
|
break;
|
|
212
217
|
}
|
|
213
218
|
}
|
|
214
|
-
return
|
|
219
|
+
return this.Store(schema);
|
|
215
220
|
}
|
|
216
221
|
/** `Standard` Makes all properties in the given object type optional */
|
|
217
|
-
Partial(
|
|
218
|
-
const
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
+
Partial(object, options = {}) {
|
|
223
|
+
const source = this.Deref(object);
|
|
224
|
+
const schema = { ...clone(source), ...options };
|
|
225
|
+
delete schema.required;
|
|
226
|
+
for (const key of Object.keys(schema.properties)) {
|
|
227
|
+
const property = schema.properties[key];
|
|
222
228
|
switch (property.modifier) {
|
|
223
229
|
case exports.ReadonlyOptionalModifier:
|
|
224
230
|
property.modifier = exports.ReadonlyOptionalModifier;
|
|
@@ -234,27 +240,29 @@ class TypeBuilder {
|
|
|
234
240
|
break;
|
|
235
241
|
}
|
|
236
242
|
}
|
|
237
|
-
return
|
|
243
|
+
return this.Store(schema);
|
|
238
244
|
}
|
|
239
245
|
/** `Standard` Picks property keys from the given object type */
|
|
240
|
-
Pick(
|
|
241
|
-
const
|
|
242
|
-
|
|
243
|
-
|
|
246
|
+
Pick(object, keys, options = {}) {
|
|
247
|
+
const source = this.Deref(object);
|
|
248
|
+
const schema = { ...clone(source), ...options };
|
|
249
|
+
schema.required = schema.required ? schema.required.filter((key) => keys.includes(key)) : undefined;
|
|
250
|
+
for (const key of Object.keys(schema.properties)) {
|
|
244
251
|
if (!keys.includes(key))
|
|
245
|
-
delete
|
|
252
|
+
delete schema.properties[key];
|
|
246
253
|
}
|
|
247
|
-
return
|
|
254
|
+
return this.Store(schema);
|
|
248
255
|
}
|
|
249
256
|
/** `Standard` Omits property keys from the given object type */
|
|
250
|
-
Omit(
|
|
251
|
-
const
|
|
252
|
-
|
|
253
|
-
|
|
257
|
+
Omit(object, keys, options = {}) {
|
|
258
|
+
const source = this.Deref(object);
|
|
259
|
+
const schema = { ...clone(source), ...options };
|
|
260
|
+
schema.required = schema.required ? schema.required.filter((key) => !keys.includes(key)) : undefined;
|
|
261
|
+
for (const key of Object.keys(schema.properties)) {
|
|
254
262
|
if (keys.includes(key))
|
|
255
|
-
delete
|
|
263
|
+
delete schema.properties[key];
|
|
256
264
|
}
|
|
257
|
-
return
|
|
265
|
+
return this.Store(schema);
|
|
258
266
|
}
|
|
259
267
|
/** `Standard` Omits the `kind` and `modifier` properties from the underlying schema */
|
|
260
268
|
Strict(schema, options = {}) {
|
|
@@ -262,43 +270,72 @@ class TypeBuilder {
|
|
|
262
270
|
}
|
|
263
271
|
/** `Extended` Creates a constructor type */
|
|
264
272
|
Constructor(args, returns, options = {}) {
|
|
265
|
-
return { ...options, kind: exports.ConstructorKind, type: 'constructor', arguments: args, returns };
|
|
273
|
+
return this.Store({ ...options, kind: exports.ConstructorKind, type: 'constructor', arguments: args, returns });
|
|
266
274
|
}
|
|
267
275
|
/** `Extended` Creates a function type */
|
|
268
276
|
Function(args, returns, options = {}) {
|
|
269
|
-
return { ...options, kind: exports.FunctionKind, type: 'function', arguments: args, returns };
|
|
277
|
+
return this.Store({ ...options, kind: exports.FunctionKind, type: 'function', arguments: args, returns });
|
|
270
278
|
}
|
|
271
279
|
/** `Extended` Creates a promise type */
|
|
272
280
|
Promise(item, options = {}) {
|
|
273
|
-
return { ...options, type: 'promise', kind: exports.PromiseKind, item };
|
|
281
|
+
return this.Store({ ...options, type: 'promise', kind: exports.PromiseKind, item });
|
|
274
282
|
}
|
|
275
283
|
/** `Extended` Creates a undefined type */
|
|
276
284
|
Undefined(options = {}) {
|
|
277
|
-
return { ...options, type: 'undefined', kind: exports.UndefinedKind };
|
|
285
|
+
return this.Store({ ...options, type: 'undefined', kind: exports.UndefinedKind });
|
|
278
286
|
}
|
|
279
287
|
/** `Extended` Creates a void type */
|
|
280
288
|
Void(options = {}) {
|
|
281
|
-
return { ...options, type: 'void', kind: exports.VoidKind };
|
|
289
|
+
return this.Store({ ...options, type: 'void', kind: exports.VoidKind });
|
|
290
|
+
}
|
|
291
|
+
/** `Standard` Creates a namespace for a set of related types */
|
|
292
|
+
Namespace($defs, options = {}) {
|
|
293
|
+
return this.Store({ ...options, kind: exports.NamespaceKind, $defs });
|
|
294
|
+
}
|
|
295
|
+
Ref(...args) {
|
|
296
|
+
if (args.length === 2) {
|
|
297
|
+
const namespace = args[0];
|
|
298
|
+
const targetKey = args[1];
|
|
299
|
+
if (namespace.$id === undefined)
|
|
300
|
+
throw new Error(`Referenced namespace has no $id`);
|
|
301
|
+
if (!this.schemas.has(namespace.$id))
|
|
302
|
+
throw new Error(`Unable to locate namespace with $id '${namespace.$id}'`);
|
|
303
|
+
return this.Store({ kind: exports.RefKind, $ref: `${namespace.$id}#/$defs/${targetKey}` });
|
|
304
|
+
}
|
|
305
|
+
else if (args.length === 1) {
|
|
306
|
+
const target = args[0];
|
|
307
|
+
if (target.$id === undefined)
|
|
308
|
+
throw new Error(`Referenced schema has no $id`);
|
|
309
|
+
if (!this.schemas.has(target.$id))
|
|
310
|
+
throw new Error(`Unable to locate schema with $id '${target.$id}'`);
|
|
311
|
+
return this.Store({ kind: exports.RefKind, $ref: target.$id });
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
throw new Error('Type.Ref: Invalid arguments');
|
|
315
|
+
}
|
|
282
316
|
}
|
|
283
317
|
/** `Experimental` Creates a recursive type */
|
|
284
318
|
Rec(callback, options = {}) {
|
|
285
319
|
const $id = options.$id || '';
|
|
286
320
|
const self = callback({ $ref: `${$id}#/$defs/self` });
|
|
287
|
-
return { ...options, $ref: `${$id}#/$defs/self`, $defs: { self } };
|
|
288
|
-
}
|
|
289
|
-
/**
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
const $
|
|
300
|
-
|
|
301
|
-
|
|
321
|
+
return this.Store({ ...options, $ref: `${$id}#/$defs/self`, $defs: { self } });
|
|
322
|
+
}
|
|
323
|
+
/** Conditionally stores and schema if it contains an $id and returns */
|
|
324
|
+
Store(schema) {
|
|
325
|
+
const $schema = schema;
|
|
326
|
+
if (!$schema['$id'])
|
|
327
|
+
return $schema;
|
|
328
|
+
this.schemas.set($schema['$id'], $schema);
|
|
329
|
+
return $schema;
|
|
330
|
+
}
|
|
331
|
+
/** Conditionally dereferences a schema if RefKind. Otherwise return argument */
|
|
332
|
+
Deref(schema) {
|
|
333
|
+
const $schema = schema;
|
|
334
|
+
if ($schema['kind'] !== exports.RefKind)
|
|
335
|
+
return schema;
|
|
336
|
+
if (!this.schemas.has($schema['$ref']))
|
|
337
|
+
throw Error(`Unable to locate schema with $id '${$schema['$ref']}'`);
|
|
338
|
+
return this.Deref(this.schemas.get($schema['$ref']));
|
|
302
339
|
}
|
|
303
340
|
}
|
|
304
341
|
exports.TypeBuilder = TypeBuilder;
|