@@ -16,24 +16,34 @@ export interface ValidationError {
16
16
readonly value : any
17
17
readonly context : Context
18
18
}
19
- export type Is < A > = ( value : any ) => value is A
20
- export type Validation < A > = Either < Array < ValidationError > , A >
21
- export type Validate < A > = ( value : any , context : Context ) => Validation < A >
22
- export type Serialize < A > = ( value : A ) => any
23
- export type Any = Type < any >
24
19
25
- type Errors = Array < ValidationError >
20
+ export type Errors = Array < ValidationError >
21
+ export type Validation < A > = Either < Errors , A >
22
+ export type Is < A > = ( value : any ) => value is A
23
+ export type Validate < S , A > = ( value : S , context : Context ) => Validation < A >
24
+ export type Serialize < S , A > = ( value : A ) => S
25
+ export type Any = Type < any , any >
26
26
27
27
export type TypeOf < RT extends Any > = RT [ '_A' ]
28
+ export type InputOf < RT extends Any > = RT [ '_S' ]
28
29
29
- export class Type < A > {
30
+ export class Type < S , A > {
30
31
readonly _A : A
32
+ readonly _S : S
31
33
constructor (
32
34
readonly name : string ,
33
35
readonly is : Is < A > ,
34
- readonly validate : Validate < A > ,
35
- readonly serialize : Serialize < A >
36
+ readonly validate : Validate < S , A > ,
37
+ readonly serialize : Serialize < S , A >
36
38
) { }
39
+ compose < B > ( ab : Type < A , B > , name ?: string ) : Type < S , B > {
40
+ return new Type (
41
+ name || `compose(${ this . name } , ${ ab . name } )` ,
42
+ ( v ) : v is B => this . is ( v ) && ab . is ( v ) ,
43
+ ( s , c ) => this . validate ( s , c ) . chain ( a => ab . validate ( a , c ) ) ,
44
+ b => this . serialize ( ab . serialize ( b ) )
45
+ )
46
+ }
37
47
}
38
48
39
49
export const getFunctionName = ( f : any ) : string => f . displayName || f . name || `<function${ f . length } >`
@@ -51,15 +61,16 @@ export const failure = <T>(value: any, context: Context): Validation<T> =>
51
61
52
62
export const success = < T > ( value : T ) : Validation < T > => new Right < Errors , T > ( value )
53
63
54
- const getDefaultContext = < T > ( type : Type < T > ) : Context => [ { key : '' , type } ]
64
+ const getDefaultContext = ( type : Any ) : Context => [ { key : '' , type } ]
55
65
56
- export const validate = < T > ( value : any , type : Type < T > ) : Validation < T > => type . validate ( value , getDefaultContext ( type ) )
66
+ export const validate = < S , A > ( value : S , type : Type < S , A > ) : Validation < A > =>
67
+ type . validate ( value , getDefaultContext ( type ) )
57
68
58
69
//
59
70
// basic types
60
71
//
61
72
62
- export class NullType extends Type < null > {
73
+ export class NullType extends Type < any , null > {
63
74
readonly _tag : 'NullType' = 'NullType'
64
75
constructor ( ) {
65
76
super ( 'null' , ( v ) : v is null => v === null , ( v , c ) => ( this . is ( v ) ? success ( v ) : failure ( v , c ) ) , identity )
@@ -69,7 +80,7 @@ export class NullType extends Type<null> {
69
80
/** An alias of `null` */
70
81
export const nullType : NullType = new NullType ( )
71
82
72
- export class UndefinedType extends Type < undefined > {
83
+ export class UndefinedType extends Type < any , undefined > {
73
84
readonly _tag : 'UndefinedType' = 'UndefinedType'
74
85
constructor ( ) {
75
86
super (
@@ -83,7 +94,7 @@ export class UndefinedType extends Type<undefined> {
83
94
84
95
const undefinedType : UndefinedType = new UndefinedType ( )
85
96
86
- export class AnyType extends Type < any > {
97
+ export class AnyType extends Type < any , any > {
87
98
readonly _tag : 'AnyType' = 'AnyType'
88
99
constructor ( ) {
89
100
super ( 'any' , ( _ ) : _ is any => true , success , identity )
@@ -92,7 +103,7 @@ export class AnyType extends Type<any> {
92
103
93
104
export const any : AnyType = new AnyType ( )
94
105
95
- export class NeverType extends Type < never > {
106
+ export class NeverType extends Type < any , never > {
96
107
readonly _tag : 'NeverType' = 'NeverType'
97
108
constructor ( ) {
98
109
super (
@@ -108,7 +119,7 @@ export class NeverType extends Type<never> {
108
119
109
120
export const never : NeverType = new NeverType ( )
110
121
111
- export class StringType extends Type < string > {
122
+ export class StringType extends Type < any , string > {
112
123
readonly _tag : 'StringType' = 'StringType'
113
124
constructor ( ) {
114
125
super (
@@ -122,7 +133,7 @@ export class StringType extends Type<string> {
122
133
123
134
export const string : StringType = new StringType ( )
124
135
125
- export class NumberType extends Type < number > {
136
+ export class NumberType extends Type < any , number > {
126
137
readonly _tag : 'NumberType' = 'NumberType'
127
138
constructor ( ) {
128
139
super (
@@ -136,7 +147,7 @@ export class NumberType extends Type<number> {
136
147
137
148
export const number : NumberType = new NumberType ( )
138
149
139
- export class BooleanType extends Type < boolean > {
150
+ export class BooleanType extends Type < any , boolean > {
140
151
readonly _tag : 'BooleanType' = 'BooleanType'
141
152
constructor ( ) {
142
153
super (
@@ -150,7 +161,7 @@ export class BooleanType extends Type<boolean> {
150
161
151
162
export const boolean : BooleanType = new BooleanType ( )
152
163
153
- export class AnyArrayType extends Type < Array < any > > {
164
+ export class AnyArrayType extends Type < any , Array < any > > {
154
165
readonly _tag : 'AnyArrayType' = 'AnyArrayType'
155
166
constructor ( ) {
156
167
super (
@@ -164,7 +175,7 @@ export class AnyArrayType extends Type<Array<any>> {
164
175
165
176
const arrayType : AnyArrayType = new AnyArrayType ( )
166
177
167
- export class AnyDictionaryType extends Type < { [ key : string ] : any } > {
178
+ export class AnyDictionaryType extends Type < any , { [ key : string ] : any } > {
168
179
readonly _tag : 'AnyDictionaryType' = 'AnyDictionaryType'
169
180
constructor ( ) {
170
181
super (
@@ -178,7 +189,7 @@ export class AnyDictionaryType extends Type<{ [key: string]: any }> {
178
189
179
190
export const Dictionary : AnyDictionaryType = new AnyDictionaryType ( )
180
191
181
- export class ObjectType extends Type < object > {
192
+ export class ObjectType extends Type < any , object > {
182
193
readonly _tag : 'ObjectType' = 'ObjectType'
183
194
constructor ( ) {
184
195
super ( 'object' , Dictionary . is , Dictionary . validate , identity )
@@ -187,7 +198,7 @@ export class ObjectType extends Type<object> {
187
198
188
199
export const object : ObjectType = new ObjectType ( )
189
200
190
- export class FunctionType extends Type < Function > {
201
+ export class FunctionType extends Type < any , Function > {
191
202
readonly _tag : 'FunctionType' = 'FunctionType'
192
203
constructor ( ) {
193
204
super (
@@ -205,7 +216,7 @@ const functionType: FunctionType = new FunctionType()
205
216
// refinements
206
217
//
207
218
208
- export class RefinementType < RT extends Any > extends Type < TypeOf < RT > > {
219
+ export class RefinementType < RT extends Any > extends Type < InputOf < RT > , TypeOf < RT > > {
209
220
readonly _tag : 'RefinementType' = 'RefinementType'
210
221
constructor (
211
222
readonly type : RT ,
@@ -233,7 +244,7 @@ export const Integer = refinement(number, n => n % 1 === 0, 'Integer')
233
244
// literal types
234
245
//
235
246
236
- export class LiteralType < V extends string | number | boolean > extends Type < V > {
247
+ export class LiteralType < V extends string | number | boolean > extends Type < any , V > {
237
248
readonly _tag : 'LiteralType' = 'LiteralType'
238
249
constructor ( readonly value : V , readonly name : string = JSON . stringify ( value ) ) {
239
250
super ( name , ( v ) : v is V => v === value , ( v , c ) => ( this . is ( v ) ? success ( value ) : failure ( v , c ) ) , identity )
@@ -247,7 +258,7 @@ export const literal = <V extends string | number | boolean>(value: V, name?: st
247
258
// keyof types
248
259
//
249
260
250
- export class KeyofType < D extends { [ key : string ] : any } > extends Type < keyof D > {
261
+ export class KeyofType < D extends { [ key : string ] : any } > extends Type < any , keyof D > {
251
262
readonly _tag : 'KeyofType' = 'KeyofType'
252
263
constructor ( readonly keys : D , readonly name : string = `(keyof ${ JSON . stringify ( Object . keys ( keys ) ) } )` ) {
253
264
super (
@@ -266,10 +277,15 @@ export const keyof = <D extends { [key: string]: any }>(keys: D, name?: string):
266
277
// recursive types
267
278
//
268
279
269
- export class RecursiveType < T > extends Type < T > {
280
+ export class RecursiveType < T > extends Type < any , T > {
270
281
readonly _tag : 'RecursiveType' = 'RecursiveType'
271
282
readonly type : Any
272
- constructor ( readonly name : string , is : Is < T > , readonly validate : Validate < T > , readonly serialize : Serialize < T > ) {
283
+ constructor (
284
+ readonly name : string ,
285
+ is : Is < T > ,
286
+ readonly validate : Validate < any , T > ,
287
+ readonly serialize : Serialize < any , T >
288
+ ) {
273
289
super ( name , is , validate , serialize )
274
290
}
275
291
}
@@ -286,7 +302,7 @@ export const recursion = <T>(name: string, definition: (self: Any) => Any): Recu
286
302
// arrays
287
303
//
288
304
289
- export class ArrayType < RT extends Any > extends Type < Array < TypeOf < RT > > > {
305
+ export class ArrayType < RT extends Any > extends Type < any , Array < TypeOf < RT > > > {
290
306
readonly _tag : 'ArrayType' = 'ArrayType'
291
307
constructor ( readonly type : RT , readonly name : string = `Array<${ type . name } >` ) {
292
308
super (
@@ -338,7 +354,7 @@ const useIdentity = (props: Props): boolean => {
338
354
return true
339
355
}
340
356
341
- export class InterfaceType < P extends Props > extends Type < InterfaceOf < P > > {
357
+ export class InterfaceType < P extends Props > extends Type < any , InterfaceOf < P > > {
342
358
readonly _tag : 'InterfaceType' = 'InterfaceType'
343
359
constructor ( readonly props : P , readonly name : string = getNameFromProps ( props ) ) {
344
360
super (
@@ -396,9 +412,9 @@ export const type = <P extends Props>(props: P, name?: string): InterfaceType<P>
396
412
// TODO remove this once https://github.com/Microsoft/TypeScript/issues/14041 is fixed
397
413
export type PartialOf < P extends Props > = { [ K in keyof P ] ?: TypeOf < P [ K ] > }
398
414
// TODO remove this once https://github.com/Microsoft/TypeScript/issues/14041 is fixed
399
- export type PartialPropsOf < P extends Props > = { [ K in keyof P ] : UnionType < [ P [ K ] , Type < undefined > ] > }
415
+ export type PartialPropsOf < P extends Props > = { [ K in keyof P ] : UnionType < [ P [ K ] , UndefinedType ] > }
400
416
401
- export class PartialType < P extends Props > extends Type < PartialOf < P > > {
417
+ export class PartialType < P extends Props > extends Type < any , PartialOf < P > > {
402
418
readonly _tag : 'PartialType' = 'PartialType'
403
419
constructor ( readonly props : P , name ?: string ) {
404
420
super (
@@ -432,7 +448,7 @@ export const partial = <P extends Props>(props: P, name?: string): PartialType<P
432
448
// dictionaries
433
449
//
434
450
435
- export class DictionaryType < C extends Any > extends Type < { [ key : string ] : TypeOf < C > } > {
451
+ export class DictionaryType < C extends Any > extends Type < any , { [ key : string ] : TypeOf < C > } > {
436
452
readonly _tag : 'DictionaryType' = 'DictionaryType'
437
453
constructor ( readonly type : C , readonly name : string = `{ [key: string]: ${ type . name } }` ) {
438
454
super (
@@ -476,7 +492,7 @@ export const dictionary = <C extends Any>(codomain: C, name?: string): Dictionar
476
492
// unions
477
493
//
478
494
479
- export class UnionType < RTS extends [ Any ] > extends Type < TypeOf < RTS [ '_A' ] > > {
495
+ export class UnionType < RTS extends [ Any ] > extends Type < any , TypeOf < RTS [ '_A' ] > > {
480
496
readonly _tag : 'UnionType' = 'UnionType'
481
497
constructor ( readonly types : RTS , readonly name : string = `(${ types . map ( type => type . name ) . join ( ' | ' ) } )` ) {
482
498
super (
@@ -512,7 +528,7 @@ export const union = <RTS extends [Any]>(types: RTS, name?: string): UnionType<R
512
528
// intersections
513
529
//
514
530
515
- export class IntersectionType < RTS extends Array < Any > , I > extends Type < I > {
531
+ export class IntersectionType < RTS extends Array < Any > , I > extends Type < any , I > {
516
532
readonly _tag : 'IntersectionType' = 'IntersectionType'
517
533
constructor ( readonly types : RTS , readonly name : string = `(${ types . map ( type => type . name ) . join ( ' & ' ) } )` ) {
518
534
super (
@@ -574,7 +590,7 @@ export function intersection<RTS extends Array<Any>>(types: RTS, name?: string):
574
590
// tuples
575
591
//
576
592
577
- export class TupleType < RTS extends Array < Any > , I > extends Type < I > {
593
+ export class TupleType < RTS extends Array < Any > , I > extends Type < any , I > {
578
594
readonly _tag : 'TupleType' = 'TupleType'
579
595
constructor ( readonly types : RTS , readonly name : string = `[${ types . map ( type => type . name ) . join ( ', ' ) } ]` ) {
580
596
super (
@@ -631,7 +647,7 @@ export function tuple<RTS extends Array<Any>>(types: RTS, name?: string): TupleT
631
647
// readonly
632
648
//
633
649
634
- export class ReadonlyType < RT extends Any > extends Type < Readonly < TypeOf < RT > > > {
650
+ export class ReadonlyType < RT extends Any > extends Type < any , Readonly < TypeOf < RT > > > {
635
651
readonly _tag : 'ReadonlyType' = 'ReadonlyType'
636
652
constructor ( readonly type : RT , readonly name : string = `Readonly<${ type . name } >` ) {
637
653
super (
@@ -655,7 +671,7 @@ export const readonly = <RT extends Any>(type: RT, name?: string): ReadonlyType<
655
671
// readonlyArray
656
672
//
657
673
658
- export class ReadonlyArrayType < RT extends Any > extends Type < ReadonlyArray < TypeOf < RT > > > {
674
+ export class ReadonlyArrayType < RT extends Any > extends Type < any , ReadonlyArray < TypeOf < RT > > > {
659
675
readonly _tag : 'ReadonlyArrayType' = 'ReadonlyArrayType'
660
676
constructor ( readonly type : RT , readonly name : string = `ReadonlyArray<${ type . name } >` ) {
661
677
super (
@@ -681,7 +697,7 @@ export class ReadonlyArrayType<RT extends Any> extends Type<ReadonlyArray<TypeOf
681
697
export const readonlyArray = < RT extends Any > ( type : RT , name ?: string ) : ReadonlyArrayType < RT > =>
682
698
new ReadonlyArrayType ( type , name )
683
699
684
- export class StrictType < P extends Props > extends Type < InterfaceOf < P > > {
700
+ export class StrictType < P extends Props > extends Type < any , InterfaceOf < P > > {
685
701
readonly _tag : 'StrictType' = 'StrictType'
686
702
constructor ( readonly props : P , name ?: string ) {
687
703
super (
0 commit comments