justus 0.0.0-alpha.0 → 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +601 -6
- package/dist/index.cjs +747 -0
- package/dist/index.cjs.map +6 -0
- package/dist/index.d.ts +504 -7
- package/dist/index.mjs +697 -0
- package/dist/index.mjs.map +6 -0
- package/package.json +19 -7
- package/src/errors.ts +126 -0
- package/src/index.ts +48 -6
- package/src/schema.ts +112 -0
- package/src/types.ts +298 -0
- package/src/utilities.ts +49 -0
- package/src/validators/any.ts +11 -0
- package/src/validators/array.ts +108 -0
- package/src/validators/boolean.ts +13 -0
- package/src/validators/constant.ts +25 -0
- package/src/validators/date.ts +78 -0
- package/src/validators/number.ts +131 -0
- package/src/validators/object.ts +126 -0
- package/src/validators/string.ts +70 -0
- package/src/validators/tuple.ts +86 -0
- package/src/validators/union.ts +81 -0
- package/dist/arrays.d.ts +0 -30
- package/dist/arrays.d.ts.map +0 -1
- package/dist/arrays.js +0 -39
- package/dist/arrays.js.map +0 -1
- package/dist/basics.d.ts +0 -32
- package/dist/basics.d.ts.map +0 -1
- package/dist/basics.js +0 -58
- package/dist/basics.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -29
- package/dist/index.js.map +0 -1
- package/dist/objects.d.ts +0 -4
- package/dist/objects.d.ts.map +0 -1
- package/dist/objects.js +0 -13
- package/dist/objects.js.map +0 -1
- package/dist/primitives.d.ts +0 -41
- package/dist/primitives.d.ts.map +0 -1
- package/dist/primitives.js +0 -16
- package/dist/primitives.js.map +0 -1
- package/dist/schemas.d.ts +0 -60
- package/dist/schemas.d.ts.map +0 -1
- package/dist/schemas.js +0 -24
- package/dist/schemas.js.map +0 -1
- package/dist/unions.d.ts +0 -19
- package/dist/unions.d.ts.map +0 -1
- package/dist/unions.js +0 -24
- package/dist/unions.js.map +0 -1
- package/dist/utils.d.ts +0 -5
- package/dist/utils.d.ts.map +0 -1
- package/dist/utils.js +0 -28
- package/dist/utils.js.map +0 -1
- package/src/arrays.ts +0 -55
- package/src/basics.ts +0 -89
- package/src/objects.ts +0 -16
- package/src/primitives.ts +0 -62
- package/src/schemas.ts +0 -166
- package/src/unions.ts +0 -112
- package/src/utils.ts +0 -24
- package/tsconfig.json +0 -17
package/README.md
CHANGED
|
@@ -7,19 +7,614 @@ JUSTUS
|
|
|
7
7
|
|
|
8
8
|
> _"**justus**"_ (latin, adj.): _"proper"_ or _"correct"_.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
JUSTUS is a very simple library for _validating_ JavaScript objects and
|
|
11
11
|
properly _annotating_ them with TypeScript types.
|
|
12
12
|
|
|
13
13
|
It focuses in providing an _easy_ and _terse_ syntax to define a simple schema,
|
|
14
14
|
used to ensure that an object is _**correct**_ and from which _**proper**_
|
|
15
15
|
typing can be inferred.
|
|
16
16
|
|
|
17
|
+
* [Quick Start](#quick-start)
|
|
18
|
+
* Validators
|
|
19
|
+
* [Strings](#string-validator)
|
|
20
|
+
* [Numbers](#number-validator)
|
|
21
|
+
* [Booleans](#boolean-validator)
|
|
22
|
+
* [Constants](#constant-validator)
|
|
23
|
+
* [Any](#any-validator)
|
|
24
|
+
* [Arrays](#array-validator)
|
|
25
|
+
* [Dates](#date-validator)
|
|
26
|
+
* [Tuples](#tuple-validator)
|
|
27
|
+
* [Objects](#object-validator) (yes, this is the important one!!!)
|
|
28
|
+
* [Any of, all of](#union-validators)
|
|
29
|
+
* [A (slightly more) complex example](#a-complex-example)
|
|
30
|
+
* [Copyright Notice](NOTICE.md)
|
|
31
|
+
* [License](LICENSE.md)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
Quick Start
|
|
35
|
+
-----------
|
|
36
|
+
|
|
37
|
+
You can use JUSTUS in your projects quite simply: import, write a schema and
|
|
38
|
+
validate. For example:
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { validate, object, string, number } from 'justus'
|
|
42
|
+
|
|
43
|
+
// Create a validator, validating _objects_ with a specific schema
|
|
44
|
+
const validator = object({
|
|
45
|
+
|
|
46
|
+
// The "foo" property in the objects to validate must be a "string"
|
|
47
|
+
// with a minimum length of one character
|
|
48
|
+
foo: string({ minLength: 1 }),
|
|
49
|
+
|
|
50
|
+
// The "bar" property in the objects to validate must be a "number"
|
|
51
|
+
bar: number,
|
|
52
|
+
|
|
53
|
+
// Always use `as const`: it correctly infers types for constants, tuples, ...
|
|
54
|
+
} as const)
|
|
55
|
+
|
|
56
|
+
// Use the validator to validate the object specified as the second argument
|
|
57
|
+
const validated = validate(validator, { foo: 'xyz', bar: 123 })
|
|
58
|
+
|
|
59
|
+
validated.foo // <-- its type will be "string"
|
|
60
|
+
validated.bar // <-- its type will be "number"
|
|
61
|
+
```
|
|
17
62
|
|
|
18
|
-
|
|
63
|
+
Easy, terse, ultimately very readable... And all types are inferred!
|
|
64
|
+
|
|
65
|
+
#### Shorthand syntax
|
|
66
|
+
|
|
67
|
+
The `validate` function (or anywhere a _validation_ is needed) can accept a
|
|
68
|
+
_shorthand_ inline syntax. From our example above:
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
const validated = validate({
|
|
72
|
+
foo: string({ minLength: 1 }),
|
|
73
|
+
bar: number,
|
|
74
|
+
} as const, {
|
|
75
|
+
foo: 'xyz',
|
|
76
|
+
bar: 123,
|
|
77
|
+
})
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
... you get the drill! See below in each _validator_ for their shorthand syntax.
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
String Validator
|
|
19
84
|
----------------
|
|
20
85
|
|
|
21
|
-
|
|
22
|
-
anything to work.
|
|
86
|
+
String validators are created using the `string` function:
|
|
23
87
|
|
|
24
|
-
|
|
25
|
-
|
|
88
|
+
```typescript
|
|
89
|
+
import { string } from 'justus'
|
|
90
|
+
|
|
91
|
+
const s1 = string() // validates any string
|
|
92
|
+
const s2 = string({ minLength: 1 }) // validate non empty strings
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
#### Options
|
|
96
|
+
|
|
97
|
+
* `minLength?: number`: The _minimum_ length of a valid `string`
|
|
98
|
+
* `maxLength?: number`: The _maximum_ length of a valid `string`
|
|
99
|
+
* `pattern?: RegExp`: A `RegExp` enforcing a particular pattern for a valid `string`
|
|
100
|
+
|
|
101
|
+
#### Branded strings
|
|
102
|
+
|
|
103
|
+
Type _branding_ can be used for string primitives. For example:
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
import { string } from 'justus'
|
|
107
|
+
|
|
108
|
+
type UUID = string & { __brand_uuid: never }
|
|
109
|
+
|
|
110
|
+
const uuidValidator = string<UUID>({
|
|
111
|
+
pattern: /^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$/,
|
|
112
|
+
minLength: 36,
|
|
113
|
+
maxLength: 36,
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
const value = validate(uuidValidator, 'C274773D-1444-41E1-9D3A-9F9D584FE8B5')
|
|
117
|
+
|
|
118
|
+
value = 'foo' // <- will fail, as "foo" is a `string`, while "value" is a `UUID`
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
#### Shorthand syntax
|
|
122
|
+
|
|
123
|
+
The shorthand syntax for string validators is simply `string`. For example:
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
import { string } from 'justus'
|
|
127
|
+
|
|
128
|
+
const validator = object({
|
|
129
|
+
foo: string // yep, no parenthesis, just "string"
|
|
130
|
+
})
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
Number Validator
|
|
135
|
+
----------------
|
|
136
|
+
|
|
137
|
+
Number validators are created using the `number` function:
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
import { number } from 'justus'
|
|
141
|
+
|
|
142
|
+
const n1 = number() // validates any number
|
|
143
|
+
const n2 = number({ minimum: 123 }) // validate numbers 123 and greater
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
#### Options
|
|
147
|
+
|
|
148
|
+
* `multipleOf?: number`: The value for which a `number` must be multiple of for it to be valid
|
|
149
|
+
* `maximum?: number`: The _inclusive_ maximum value for a valid `number`
|
|
150
|
+
* `minimum?: number`: The _inclusive_ minimum value for a valid `number`
|
|
151
|
+
* `exclusiveMaximum?: number`: The _exclusive_ maximum value for a valid `number`
|
|
152
|
+
* `exclusiveMinimum?: number`: The _exclusive_ minimum value for a valid `number`
|
|
153
|
+
* `allowNaN?: boolean`: Whether to allow `NaN` or not (default: `false`)
|
|
154
|
+
|
|
155
|
+
#### Branded numbers
|
|
156
|
+
|
|
157
|
+
Type _branding_ can be used for number primitives. For example:
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
import { number } from 'justus'
|
|
161
|
+
|
|
162
|
+
type Price = string & { __brand_price: never }
|
|
163
|
+
|
|
164
|
+
const priceValidator = number<Price>({
|
|
165
|
+
multipleOf: 0.01, // cents, anyone? :-)
|
|
166
|
+
minimum: 0, // no negative prices, those are _discounts_
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
const value = validate(priceValidator, 123.45)
|
|
170
|
+
|
|
171
|
+
value = 432 // <- will fail, as 432 is a `number`, while "value" is a `Price`
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
#### Shorthand syntax
|
|
175
|
+
|
|
176
|
+
The shorthand syntax for number validators is simply `number`. For example:
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
import { number } from 'justus'
|
|
180
|
+
|
|
181
|
+
const validator = object({
|
|
182
|
+
foo: number // yep, no parenthesis, just "number"
|
|
183
|
+
})
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
Boolean Validator
|
|
188
|
+
-----------------
|
|
189
|
+
|
|
190
|
+
The boolean validator is represented by the `boolean` constant:
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
import { boolean, object } from 'justus'
|
|
194
|
+
|
|
195
|
+
const validator = object({
|
|
196
|
+
foo: boolean // it's a constant, no options!
|
|
197
|
+
})
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
Constant Validator
|
|
202
|
+
------------------
|
|
203
|
+
|
|
204
|
+
Cosntant validators are created using the `constant` function:
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
import { constant } from 'justus'
|
|
208
|
+
|
|
209
|
+
const c1 = constant('foo') // validates the `string` constant "foo"
|
|
210
|
+
const c2 = constant(12345) // validates the `number` constant 12345
|
|
211
|
+
const c3 = constant(false) // validates the `boolean` constant `false`
|
|
212
|
+
const c4 = constant(null) // validates the `null` constant
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
The constant validator requires a `string`, `number`, `boolean` or `null`
|
|
216
|
+
constant.
|
|
217
|
+
|
|
218
|
+
#### Shorthand syntax
|
|
219
|
+
|
|
220
|
+
The shorthand syntax for constant validators is simply its value. For example:
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
import { object, validate } from 'justus'
|
|
224
|
+
|
|
225
|
+
const validator = object({
|
|
226
|
+
foo: 'foo', // the string "foo"
|
|
227
|
+
bar: 12345, // the number 12345
|
|
228
|
+
baz: false, // the boolean false
|
|
229
|
+
nil: null, // the null constant
|
|
230
|
+
} as const) // yep, don't forget "as const" to infer types correctly
|
|
231
|
+
|
|
232
|
+
const result = validate(validator, something)
|
|
233
|
+
|
|
234
|
+
result.foo // <- its type will be `"foo"` (or "string" if you didn't use "as const")
|
|
235
|
+
result.bar // <- its type will be `12345` (or "number" if you didn't use "as const")
|
|
236
|
+
result.baz // <- its type will be `false` (or "boolean" if you didn't use "as const")
|
|
237
|
+
result.nil // <- its type will be `null` (or "any" if you didn't use "as const")
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
Any Validator
|
|
242
|
+
-------------
|
|
243
|
+
|
|
244
|
+
The _any_ validator is represented by the `any` constant:
|
|
245
|
+
|
|
246
|
+
```typescript
|
|
247
|
+
import { any, object, validate } from 'justus'
|
|
248
|
+
|
|
249
|
+
const validator = object({
|
|
250
|
+
foo: any // it's a constant, no options!
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
const result = validate(validator, something)
|
|
254
|
+
|
|
255
|
+
result.foo // <- its type will be `any`
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
Array Validator
|
|
260
|
+
---------------
|
|
261
|
+
|
|
262
|
+
Array validators are created using the `array` or `arrayOf` functions:
|
|
263
|
+
|
|
264
|
+
```typescript
|
|
265
|
+
import { array, arrayOf, number, string } from 'justus'
|
|
266
|
+
|
|
267
|
+
const a1 = array() // validates any array
|
|
268
|
+
const a2 = string({ maxItems: 10, items: string }) // array of strings
|
|
269
|
+
const a3 = arrayOf(number) // array of numbers
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
#### Options
|
|
273
|
+
|
|
274
|
+
* `maxItems?: number`: The _maximum_ number of elements a valid `Array`
|
|
275
|
+
* `minItems?: number`: The _minimum_ number of elements a valid `Array`
|
|
276
|
+
* `uniqueItems?: boolean`: Indicates if the `Array`'s elements must be unique
|
|
277
|
+
* `items?: V`: The _type_ of each individual item in the `Array` */
|
|
278
|
+
|
|
279
|
+
#### Shorthand syntax
|
|
280
|
+
|
|
281
|
+
The shorthand syntax for string validators is simply `array`. For example:
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
import { array } from 'justus'
|
|
285
|
+
|
|
286
|
+
const validator = object({
|
|
287
|
+
foo: array // validate any array, of any length, containing anything
|
|
288
|
+
})
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
The `arrayOf` function can also be considered a _shorthand_ of the full
|
|
292
|
+
`array({ items: ... })`. For example the two following declarations are
|
|
293
|
+
equivalent:
|
|
294
|
+
|
|
295
|
+
```typescript
|
|
296
|
+
import { array, arrayOf } from 'justus'
|
|
297
|
+
|
|
298
|
+
const a1 = array({ items: string })
|
|
299
|
+
const a2 = arrayOf(string) // same as above, just more readable
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
Date Validator
|
|
304
|
+
--------------
|
|
305
|
+
|
|
306
|
+
Date validators are created using the `date` function:
|
|
307
|
+
|
|
308
|
+
```typescript
|
|
309
|
+
import { date } from 'justus'
|
|
310
|
+
|
|
311
|
+
const d1 = date() // validates any date
|
|
312
|
+
const d2 = date({ format: 'iso' }) // validate ISO dates
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
> **NOTE:** Date validators also _convert_ dates (in string format), or
|
|
316
|
+
> timestamps (milliseconds from the epoch) into proper `Date` instances.
|
|
317
|
+
|
|
318
|
+
#### Options
|
|
319
|
+
|
|
320
|
+
* `format?: 'iso' | 'timestamp'`: The format for dates, `iso` for _ISO Dates_
|
|
321
|
+
(as outlined in RFC 3339) or `timestamp` for the number of milliseconds since
|
|
322
|
+
the epoch
|
|
323
|
+
* `from?: Date`: The earliest value a date can have
|
|
324
|
+
* `until?: Date`: The latest value a date can have
|
|
325
|
+
|
|
326
|
+
#### Shorthand syntax
|
|
327
|
+
|
|
328
|
+
The shorthand syntax for number validators is simply `date`. For example:
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
import { date } from 'justus'
|
|
332
|
+
|
|
333
|
+
const validator = object({
|
|
334
|
+
foo: date // anything that can be converted to `Date` will be!
|
|
335
|
+
})
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
Tuple Validator
|
|
340
|
+
---------------
|
|
341
|
+
|
|
342
|
+
A _tuple_ is (by definition) _a finite ordered list (sequence) of elements_.
|
|
343
|
+
|
|
344
|
+
Tuple validators are created using the `tuple` function:
|
|
345
|
+
|
|
346
|
+
```typescript
|
|
347
|
+
import { tuple, string, number, boolean } from 'justus'
|
|
348
|
+
|
|
349
|
+
// Validates 3 elements tuple: (in order) a string, a number and a boolean
|
|
350
|
+
const t1 = tuple([ string, number, boolean ])
|
|
351
|
+
|
|
352
|
+
// Validates a tuple whose first element is a string, followed by zero or more
|
|
353
|
+
// numbers, and wholse last element is a boolean
|
|
354
|
+
const t2 = tuple([ string, ...number, boolean ]) // yay! rest parameters!
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
As shown above, any `Validator` (or one of its shorthands) can be used as a
|
|
358
|
+
_rest parameter_ implying zero or more elements of the specified kind.
|
|
359
|
+
|
|
360
|
+
A more complext example:
|
|
361
|
+
|
|
362
|
+
```typescript
|
|
363
|
+
import { tuple, string, number, object } from 'justus'
|
|
364
|
+
|
|
365
|
+
const myObject = object({
|
|
366
|
+
version: number,
|
|
367
|
+
title: string,
|
|
368
|
+
})
|
|
369
|
+
|
|
370
|
+
// This is the silliest tuple ever written, but outlines our intentions:
|
|
371
|
+
const sillyTuple = tuple([ 'start', ...myObject, 'end' ] as const)
|
|
372
|
+
|
|
373
|
+
// Validate using our tuple:
|
|
374
|
+
validate(tuple, [
|
|
375
|
+
'start', // yep, a constant
|
|
376
|
+
{ version: 1, title: 'Hello world' },
|
|
377
|
+
{ version: 2, title: 'Foo, bar and baz' },
|
|
378
|
+
'end', // the last
|
|
379
|
+
])
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
Object Validator
|
|
384
|
+
----------------
|
|
385
|
+
|
|
386
|
+
As seen in the examples above, object validators are created using the
|
|
387
|
+
`object` function:
|
|
388
|
+
|
|
389
|
+
```typescript
|
|
390
|
+
import { object, string, number, boolean } from 'justus'
|
|
391
|
+
|
|
392
|
+
const o1 = object() // any object (excluding null - darn JavaScript)
|
|
393
|
+
const o2 = object({
|
|
394
|
+
foo: string, // any string
|
|
395
|
+
bar: number, // any number
|
|
396
|
+
baz: 'Hello, world!', // the constant "Hello, world!"
|
|
397
|
+
} as const)
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
#### Shorthand syntax
|
|
401
|
+
|
|
402
|
+
The shorthand syntax for object validators is simply `object`. For example:
|
|
403
|
+
|
|
404
|
+
```typescript
|
|
405
|
+
import { arrayOf, object } from 'justus'
|
|
406
|
+
|
|
407
|
+
const validator = arrayOf(object) // won't validate if the array has numbers, strings, ...
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
#### Allow additional properties
|
|
411
|
+
|
|
412
|
+
Sometimes it's necessary to allow additional properties in an object.
|
|
413
|
+
|
|
414
|
+
Destructuring `...allowAdditionalProperties` in an objects does the trick!
|
|
415
|
+
|
|
416
|
+
```typescript
|
|
417
|
+
import { object, string, number, boolean } from 'justus'
|
|
418
|
+
|
|
419
|
+
const o1 = object({
|
|
420
|
+
foo: string, // any string
|
|
421
|
+
bar: number, // any number
|
|
422
|
+
...allowAdditionalProperties, // any other key will be "any"
|
|
423
|
+
})
|
|
424
|
+
|
|
425
|
+
const result1 = validate(o1, ... some object ...)
|
|
426
|
+
|
|
427
|
+
result1.foo // <-- this will be a "string"
|
|
428
|
+
result1.bar // <-- this will be a "number"
|
|
429
|
+
result1.baz // <-- additional property, this will be "any"
|
|
430
|
+
|
|
431
|
+
// additional properties with a type
|
|
432
|
+
|
|
433
|
+
const o2 = object({
|
|
434
|
+
foo: string, // any string
|
|
435
|
+
bar: number, // any number
|
|
436
|
+
...allowAdditionalProperties(boolean), // any other key will be "boolean"
|
|
437
|
+
})
|
|
438
|
+
|
|
439
|
+
const result2 = validate(o2, ... some object ...)
|
|
440
|
+
|
|
441
|
+
result2.foo // <-- this will be a "string"
|
|
442
|
+
result2.bar // <-- this will be a "number"
|
|
443
|
+
result2.baz // <-- additional property, this will be "boolean"
|
|
444
|
+
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
Here `allowAdditionalProperties` is also a function, which can take some
|
|
448
|
+
parameters to configure its behaviour:
|
|
449
|
+
|
|
450
|
+
* `...allowAdditionalProperties`: default shorthand, allows additional
|
|
451
|
+
properties and will infer the `any` type for them.
|
|
452
|
+
* `...allowAdditionalProperties()`: as a function, and same as above, it allows
|
|
453
|
+
additional properties and will infer the `any` type for them.
|
|
454
|
+
* `...allowAdditionalProperties(true)`: as a function, and same as above, it
|
|
455
|
+
allows additional properties and will infer the `any` type for them.
|
|
456
|
+
* `...allowAdditionalProperties(false)`: as a function, it _forbids_ any
|
|
457
|
+
additional property in objects, useful when extending objects (see below)
|
|
458
|
+
* `...allowAdditionalProperties(... type ...)`: as a function, it allows
|
|
459
|
+
additional properties in objects and ensures their type is correct
|
|
460
|
+
|
|
461
|
+
#### Extending objects
|
|
462
|
+
|
|
463
|
+
Simply destructure one into another. For example:
|
|
464
|
+
|
|
465
|
+
```typescript
|
|
466
|
+
import { object, string, number, boolean } from 'justus'
|
|
467
|
+
|
|
468
|
+
const o1 = object({
|
|
469
|
+
foo: string, // any string
|
|
470
|
+
bar: number, // any number
|
|
471
|
+
})
|
|
472
|
+
|
|
473
|
+
const o2 = object({
|
|
474
|
+
...o1, // anything part of "o1" will be here as well!
|
|
475
|
+
bar: boolean, // here "bar" is no longer a number, but a boolean
|
|
476
|
+
baz: number, // add the "baz" property as a number
|
|
477
|
+
} as const)
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
A slightly more complex scenario arises when considering additional properties
|
|
481
|
+
in the base object, but forcedly forbidding them in an extend one.
|
|
482
|
+
|
|
483
|
+
To do so, simply override in the extended object as follows:
|
|
484
|
+
|
|
485
|
+
Simply destructure one into another. For example:
|
|
486
|
+
|
|
487
|
+
```typescript
|
|
488
|
+
import { object, string, number, boolean } from 'justus'
|
|
489
|
+
|
|
490
|
+
const o1 = object({
|
|
491
|
+
foo: string, // any string
|
|
492
|
+
bar: number, // any number
|
|
493
|
+
...allowAdditionalProperties(boolean), // any other property is a boolean
|
|
494
|
+
})
|
|
495
|
+
|
|
496
|
+
const o2 = object({
|
|
497
|
+
...o1, // anything part of "o1" will be here as well!
|
|
498
|
+
baz: boolean, // add "baz" to "o1", forcing it to be a "boolean"
|
|
499
|
+
...allowAdditionaProperties(false), // no more additional properties here!
|
|
500
|
+
} as const)
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
#### Optional and read-only properties
|
|
504
|
+
|
|
505
|
+
Optional and read-only properties can also be declared in objects:
|
|
506
|
+
|
|
507
|
+
```typescript
|
|
508
|
+
import { object, readonly, optional, string, number, boolean } from 'justus'
|
|
509
|
+
|
|
510
|
+
const o1 = object({
|
|
511
|
+
foo: string, // any string, but must be a string
|
|
512
|
+
bar: optional(number), // optional property as "number | undefined"
|
|
513
|
+
baz: readonly(boolean), // read-only property as "readonly boolean"
|
|
514
|
+
xxx: readonly(optional(string)) // ... guess what it'll be?
|
|
515
|
+
})
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
Union Validators
|
|
520
|
+
----------------
|
|
521
|
+
|
|
522
|
+
Unions (either _all_ or _any_) are defined using the `allOf` or `oneOf`
|
|
523
|
+
functions.
|
|
524
|
+
|
|
525
|
+
To make sure all validations pass use `allOf`:
|
|
526
|
+
|
|
527
|
+
```typescript
|
|
528
|
+
import { allOf, object, string, number } from 'justus'
|
|
529
|
+
|
|
530
|
+
const o1 = object({ foo: string })
|
|
531
|
+
const o2 = object({ bar: number })
|
|
532
|
+
|
|
533
|
+
const result = validate(allOf(o1, o2), ... some object ...)
|
|
534
|
+
// result here will have the type of what's inferred by o1 _and_ o2
|
|
535
|
+
|
|
536
|
+
result.foo // <-- this is a "string"
|
|
537
|
+
result.bar // <-- this is a "number"
|
|
538
|
+
|
|
539
|
+
// be careful about never!
|
|
540
|
+
const result2 = validate(allOf(number, string), ... some primitive ...)
|
|
541
|
+
// obviously "result2" will be of type "never" as "number" and "string" do not match!
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
More useful, to make sure all validations pass use `oneOf`:
|
|
545
|
+
|
|
546
|
+
```typescript
|
|
547
|
+
import { oneOf, string, number } from 'justus'
|
|
548
|
+
|
|
549
|
+
const result = validate(oneOf(number, string), ... some primitive ...)
|
|
550
|
+
|
|
551
|
+
result // <-- its type will be "number | string"
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
A complex example
|
|
556
|
+
-----------------
|
|
557
|
+
|
|
558
|
+
This example is not complicated at all, but it outlines the simplicity
|
|
559
|
+
of the syntax intended for JUSTUS.
|
|
560
|
+
|
|
561
|
+
Let's assume we have some _time series_ data, but we can expect this in a
|
|
562
|
+
couple of different flavors, either V1 or V2 with some subtle differences:
|
|
563
|
+
|
|
564
|
+
```typescript
|
|
565
|
+
import {
|
|
566
|
+
arrayOf,
|
|
567
|
+
date,
|
|
568
|
+
number,
|
|
569
|
+
object,
|
|
570
|
+
oneOf,
|
|
571
|
+
string,
|
|
572
|
+
tuple,
|
|
573
|
+
validate,
|
|
574
|
+
} from '../src'
|
|
575
|
+
|
|
576
|
+
// Our V1 time-series tuple is simply a timestamp followed by a numeric value
|
|
577
|
+
const entryv1 = tuple([ date, number ] as const)
|
|
578
|
+
|
|
579
|
+
// Our V1 response from the time series database declares the version and data
|
|
580
|
+
const responsev1 = object({
|
|
581
|
+
version: 1,
|
|
582
|
+
entries: arrayOf(entryv1),
|
|
583
|
+
} as const)
|
|
584
|
+
|
|
585
|
+
// Our V2 time-series tuple is a timestamp, followed by a number and zero or
|
|
586
|
+
// more strings indicating some remarks on the measurements
|
|
587
|
+
const entryv2 = tuple([ date, number, ...string ] as const)
|
|
588
|
+
|
|
589
|
+
// Response for V2 is the same as V1, with some extra stuff
|
|
590
|
+
const responsev2 = object({
|
|
591
|
+
version: 2,
|
|
592
|
+
entries: arrayOf(entryv2),
|
|
593
|
+
average: number, // this is extra!
|
|
594
|
+
} as const)
|
|
595
|
+
|
|
596
|
+
// Our combined response is either V1 or V2
|
|
597
|
+
const response = oneOf(responsev1, responsev2)
|
|
598
|
+
|
|
599
|
+
// GO! Validate!
|
|
600
|
+
const result = validate(response, {})
|
|
601
|
+
|
|
602
|
+
if (result.version === 1) {
|
|
603
|
+
result.version // the type here will be the literal number 1
|
|
604
|
+
result.entries.forEach((entry) => {
|
|
605
|
+
entry[0] // this will be a `Date` instance
|
|
606
|
+
entry[1] // this will be a "number"
|
|
607
|
+
// entry[2] // will generate a typescript error
|
|
608
|
+
})
|
|
609
|
+
// response.average // will generate a typescript error
|
|
610
|
+
} else {
|
|
611
|
+
result.version // the type here will be the literal number 2
|
|
612
|
+
result.entries.forEach((entry) => {
|
|
613
|
+
entry[0] // this will be a `Date` instance
|
|
614
|
+
entry[1] // this will be a "number"
|
|
615
|
+
entry[2] // this will be a "string"
|
|
616
|
+
entry[999] // this too will be a "string"
|
|
617
|
+
})
|
|
618
|
+
result.average // this will be a "number""
|
|
619
|
+
}
|
|
620
|
+
```
|