@@ -26,23 +26,31 @@ The validation errors are detailed. Adapted from the brilliant work in `flow-run
26
26
- [ ` t.symbol() ` ] ( #tsymbol )
27
27
- [ ` t.symbol(MySymbol) ` ] ( #tsymbolmysymbol )
28
28
- [ ` t.null() ` / ` t.nullLiteral() ` ] ( #tnull--tnullliteral )
29
+ - [ ` t.nullOr(t.string()) ` ] ( #tnullortstring )
29
30
- [ ` t.undefined() ` / ` t.undefinedLiteral() ` ] ( #tundefined--tundefinedliteral )
30
31
- [ ` t.nullish() ` ] ( #tnullish )
31
- - [ ` t.nullOr (t.string()) ` ] ( #tnullortstring )
32
+ - [ ` t.nullishOr (t.string()) ` ] ( #tnullishortstring )
32
33
- [ ` t.array(t.number()) ` ] ( #tarraytnumber )
33
34
- [ ` t.simpleObject({ foo: t.string() }) ` ] ( #tsimpleobject-foo-tstring- )
34
35
- [ ` t.object ` ] ( #tobject )
35
36
- [ ` t.record(t.string(), t.number()) ` ] ( #trecordtstring-tnumber )
36
37
- [ ` t.tuple(t.string(), t.number()) ` ] ( #ttupletstring-tnumber )
37
38
- [ ` t.intersection(A, B) ` ] ( #tintersectiona-b )
38
39
- [ ` t.union(t.string(), t.number()) ` ] ( #tuniontstring-tnumber )
39
- - [ ` t.constrain(type, ...constraints) ` ] ( #tconstraintype-constraints )
40
- - [ ` t.Type ` ] ( #ttype )
40
+ - [ ` t.alias(name, type) ` ] ( #taliasname-type )
41
+ - [ ` t.ref(() => typeAlias) ` ] ( #tref--typealias )
42
+ - [ ` t.Type<T> ` ] ( #ttype )
41
43
- [ ` accepts(input: any): boolean ` ] ( #acceptsinput-any-boolean )
42
- - [ ` assert(input: any, prefix = '', path?: (string | number | symbol)[]): V ` ] ( #assertinput-any-prefix---path-string--number--symbol-v )
43
- - [ ` validate(input: any, prefix = '', path?: (string | number | symbol)[]): Validation ` ] ( #validateinput-any-prefix---path-string--number--symbol-validation )
44
+ - [ ` assert<V extends T> (input: any, prefix = '', path?: (string | number | symbol)[]): V ` ] ( #assertinput-any-prefix---path-string--number--symbol-v )
45
+ - [ ` validate(input: any, prefix = '', path?: (string | number | symbol)[]): Validation<T> ` ] ( #validateinput-any-prefix---path-string--number--symbol-validation )
44
46
- [ ` warn(input: any, prefix = '', path?: (string | number | symbol)[]): void ` ] ( #warninput-any-prefix---path-string--number--symbol-void )
45
47
- [ ` toString(): string ` ] ( #tostring-string )
48
+ - [ ` t.ExtractType<T extends Type<any>> ` ] ( #textracttype )
49
+ - [ ` t.TypeAlias<T> ` ] ( #ttypealias )
50
+ - [ ` readonly name: string ` ] ( #readonly-name-string )
51
+ - [ ` addConstraint(...constraints: TypeConstraint<T>[]): this ` ] ( #addconstraintconstraints-typeconstraint-this )
52
+ - [ Custom Constraints] ( #custom-constraints )
53
+ - [ Recursive Types] ( #recursive-types )
46
54
47
55
<!-- tocstop -->
48
56
@@ -99,7 +107,7 @@ const example: Post = PostValidator.assert({
99
107
})
100
108
```
101
109
102
- Hover over ` Post ` in the IDE and you'll see, voilà:
110
+ Hover over ` Post ` in VSCode and you'll see, voilà:
103
111
104
112
``` ts
105
113
type Post = {
@@ -199,6 +207,10 @@ A validator that requires the value to be `MySymbol`.
199
207
200
208
A validator that requires the value to be ` null ` .
201
209
210
+ ### ` t.nullOr(t.string()) `
211
+
212
+ A validator that requires the value to be ` string | null `
213
+
202
214
### ` t.undefined() ` / ` t.undefinedLiteral() `
203
215
204
216
A validator that requires the value to be ` undefined ` .
@@ -207,9 +219,9 @@ A validator that requires the value to be `undefined`.
207
219
208
220
A validator that requires the value to be ` null | undefined ` .
209
221
210
- ### ` t.nullOr (t.string()) `
222
+ ### ` t.nullishOr (t.string()) `
211
223
212
- A validator that requires the value to be ` string | null `
224
+ A validator that requires the value to be ` string | null | undefined ` .
213
225
214
226
### ` t.array(t.number()) `
215
227
@@ -269,20 +281,18 @@ CommentedThingType.assert({ name: 'foo', comment: 'sweet' })
269
281
270
282
A validator that requires the value to be ` string | number ` . Accepts a variable number of arguments, though type generation is only overloaded up to 8 arguments.
271
283
272
- ### ` t.constrain(type, ...constraints ) `
284
+ ### ` t.alias(name, type ) `
273
285
274
- Applies custom constraints to a type. For example:
286
+ Creates a ` TypeAlias ` with the given ` name ` and ` type ` .
275
287
276
- ``` ts
277
- const PositiveNumber = t .constrain (t .number (), (value : number ):
278
- | string
279
- | null
280
- | undefined => {
281
- if (value < 0 ) return ' must be >= 0'
282
- })
288
+ Type aliases serve two purposes:
283
289
284
- PositiveNumber .assert (- 1 ) // throws an error including "must be >= 0"
285
- ```
290
+ - They allow you to [ create recursive type validators with ` t.ref() ` ] ( #recursive-types )
291
+ - You can [ add custom constraints to them] ( #custom-constraints )
292
+
293
+ ### ` t.ref(() => typeAlias) `
294
+
295
+ Creates a reference to the given ` TypeAlias ` . See [ Recursive Types] ( #recursive-types ) for examples.
286
296
287
297
## ` t.Type<T> `
288
298
@@ -348,3 +358,95 @@ type Post = {
348
358
tags: string []
349
359
}
350
360
` ` `
361
+
362
+ ## ` t .TypeAlias <T >`
363
+
364
+ ### ` readonly name : string `
365
+
366
+ The name of the alias.
367
+
368
+ ### ` addConstraint (... constraints : TypeConstraint <T >[]): this `
369
+
370
+ Adds custom constraints. ` TypeConstraint <T >` is a function ` (value : T ) => string | null | undefined ` which
371
+ returns nullish if ` value ` is valid, or otherwise a ` string ` describing why ` value ` is invalid.
372
+
373
+ ## Custom Constraints
374
+
375
+ It's nice to be able to validate that something is a ` number ` , but what if we want to make sure it's positive?
376
+ We can do this by creating a type alias for ` number ` and adding a custom constraint to it:
377
+
378
+ ` ` ` ts
379
+ const PositiveNumberType = t
380
+ .alias (' PositiveNumber' , t .number ())
381
+ .addConstraint ((value : number ) => (value > 0 ? undefined : ' must be > 0' ))
382
+
383
+ PositiveNumberType .assert (- 1 )
384
+ ```
385
+
386
+ The assertion will throw a ` t.RuntimeTypeError ` with the following message:
387
+
388
+ ```
389
+ Value must be > 0
390
+
391
+ Expected: PositiveNumber
392
+
393
+ Actual Value: ${value}
394
+
395
+ Actual Type: number
396
+ ```
397
+
398
+ ## Recursive Types
399
+
400
+ Creating validators for recursive types takes a bit of extra effort. Naively, we would want to do this:
401
+
402
+ ``` ts
403
+ const NodeType = t .object <{
404
+ value: any
405
+ left? : any
406
+ right? : any
407
+ }>()({
408
+ value: t .any (),
409
+ left: t .optional (NodeType ),
410
+ right: t .optional (NodeType ),
411
+ })
412
+ ```
413
+
414
+ But ` t.optional(NodeType) ` causes the error ` Block-scoped variable 'NodeType' referenced before its declaration ` .
415
+
416
+ To work around, this we can create a ` TypeAlias ` and a reference to it:
417
+
418
+ ``` ts
419
+ const NodeType: t .TypeAlias <{
420
+ value: any
421
+ left? : Node
422
+ right? : Node
423
+ }> = t .alias (
424
+ ' Node' ,
425
+ t .object <{
426
+ value: any
427
+ left? : any
428
+ right? : any
429
+ }>()({
430
+ value: t .any (),
431
+ left: t .optional (t .ref (() => NodeType )),
432
+ right: t .optional (t .ref (() => NodeType )),
433
+ })
434
+ )
435
+
436
+ type Node = t .ExtractType <typeof NodeType >
437
+
438
+ NodeType .assert ({
439
+ value: ' foo' ,
440
+ left: {
441
+ value: 2 ,
442
+ right: {
443
+ value: 3 ,
444
+ },
445
+ },
446
+ right: {
447
+ value: 6 ,
448
+ },
449
+ })
450
+ ```
451
+
452
+ Notice how we use a thunk function in ` t.ref(() => NodeType) ` to avoid referencing ` NodeType ` before its declaration.
0 commit comments