1- type Opt < T > = T & { optional : true } ;
2- type Req < T > = T & { optional : false } ;
3- type V < T > = Opt < T > | Req < T > ;
1+ type O < V > = V & { optional : true } ;
2+ type R < V > = V & { optional : false } ;
3+ type V < V > = O < V > | R < V > ;
44
55interface StringOptions {
66 type : 'string' ;
77 maxLength ?: [ number , string ] ;
88 minLength ?: [ number , string ] ;
99 pattern ?: [ RegExp , string ] ;
1010}
11- type StringValidator = V < StringOptions > ;
1211
1312interface NumberOptions {
1413 type : 'number' ;
1514 max ?: [ number , string ] ;
1615 min ?: [ number , string ] ;
1716 is ?: [ 'integer' | 'float' , string ] ;
1817}
19- type NumberValidator = V < NumberOptions > ;
2018
21- interface BooleanValidator {
19+ interface BooleanOptions {
2220 type : 'boolean' ;
23- optional : boolean ;
2421}
2522
26- interface ListValidator < T extends Validator [ ] > {
23+ interface ListOptions < T > {
2724 type : 'list' ;
28- optional : boolean ;
2925 shape : T ;
3026}
3127
32- interface ListofValidator < T extends Validator > {
28+ interface ListofOptions < T > {
3329 type : 'listof' ;
34- optional : boolean ;
3530 of : T ;
3631}
3732
38- interface RecordValidator < T extends Schema > {
33+ interface RecordOptions < T > {
3934 type : 'record' ;
40- optional : boolean ;
4135 shape : T ;
4236}
4337
44- interface RecordofValidator < T extends Validator > {
38+ interface RecordofOptions < T > {
4539 type : 'recordof' ;
46- optional : boolean ;
4740 of : T ;
4841}
4942
43+ type BooleanValidator = V < BooleanOptions > ;
44+ type StringValidator = V < StringOptions > ;
45+ type NumberValidator = V < NumberOptions > ;
46+ type ListValidator < T extends Validator [ ] > = V < ListOptions < T > > ;
47+ type ListofValidator < T extends Validator > = V < ListofOptions < T > > ;
48+ type RecordValidator < T extends Schema > = V < RecordOptions < T > > ;
49+ type RecordofValidator < T extends Validator > = V < RecordofOptions < T > > ;
50+
5051type Validator =
5152 | StringValidator
5253 | NumberValidator
5354 | BooleanValidator
54- | ListofValidator < Validator >
55- | ListValidator < Validator [ ] >
56- | RecordValidator < Schema >
57- | RecordofValidator < Validator > ;
55+ | ListofValidator < any >
56+ | ListValidator < any [ ] >
57+ | RecordValidator < any >
58+ | RecordofValidator < any > ;
5859
5960interface Schema {
6061 [ key : string ] : Validator ;
@@ -64,81 +65,107 @@ type DataFrom<S extends Schema> = {
6465 [ K in keyof S ] : InferDataType < S [ K ] > ;
6566} ;
6667
67- type InferTypeWithOptional < T , U > = T extends Opt < T > ? U | void : U ;
68+ type InferTypeWithOptional < T , U > = T extends O < T > ? U | undefined : U ;
6869
6970type InferDataType < T > = T extends StringValidator
7071 ? InferTypeWithOptional < T , string >
7172 : T extends NumberValidator
7273 ? InferTypeWithOptional < T , number >
7374 : T extends BooleanValidator
74- ? boolean
75+ ? InferTypeWithOptional < T , boolean >
7576 : T extends ListValidator < infer U >
76- ? { [ K in keyof U ] : InferDataType < U [ K ] > }
77+ ? InferTypeWithOptional < T , { [ K in keyof U ] : InferDataType < U [ K ] > } >
7778 : T extends ListofValidator < infer V >
78- ? InferDataType < V > [ ]
79+ ? InferTypeWithOptional < T , InferDataType < V > [ ] >
7980 : T extends RecordValidator < infer S >
80- ? { [ K in keyof S ] : InferDataType < S [ K ] > }
81+ ? InferTypeWithOptional < T , { [ K in keyof S ] : InferDataType < S [ K ] > } >
8182 : T extends RecordofValidator < infer V >
82- ? Record < string , InferDataType < V > >
83+ ? InferTypeWithOptional < T , { [ key : string ] : InferDataType < V > } >
8384 : never ;
8485
85- function string ( config : { optional : true } & Omit < StringOptions , 'type' > ) : Opt < StringOptions > ;
86- function string ( config : { optional : false } & Omit < StringOptions , 'type' > ) : Req < StringOptions > ;
87- function string ( config : Omit < StringOptions , 'type' > ) : Req < StringOptions > ;
88- function string ( ) : Req < StringOptions > ;
86+ // helpers
87+ function string ( config : { optional : true } & Omit < StringOptions , 'type' > ) : O < StringOptions > ;
88+ function string ( config : { optional : false } & Omit < StringOptions , 'type' > ) : R < StringOptions > ;
89+ function string ( config : Omit < StringOptions , 'type' > ) : R < StringOptions > ;
90+ function string ( ) : R < StringOptions > ;
8991function string ( config ?: { optional ?: boolean } & Omit < StringOptions , 'type' > ) : StringValidator {
9092 return {
9193 type : 'string' ,
9294 optional : Boolean ( config ?. optional ) ,
9395 } ;
9496}
9597
96- function number ( config : { optional : true } & Omit < NumberOptions , 'type' > ) : Opt < NumberOptions > ;
97- function number ( config : { optional : false } & Omit < NumberOptions , 'type' > ) : Req < NumberOptions > ;
98- function number ( config : Omit < NumberOptions , 'type' > ) : Req < NumberOptions > ;
99- function number ( ) : Req < NumberOptions > ;
98+ function number ( config : { optional : true } & Omit < NumberOptions , 'type' > ) : O < NumberOptions > ;
99+ function number ( config : { optional : false } & Omit < NumberOptions , 'type' > ) : R < NumberOptions > ;
100+ function number ( config : Omit < NumberOptions , 'type' > ) : R < NumberOptions > ;
101+ function number ( ) : R < NumberOptions > ;
100102function number ( config ?: { optional ?: boolean } & Omit < NumberOptions , 'type' > ) : NumberValidator {
101103 return {
102104 type : 'number' ,
103105 optional : Boolean ( config ?. optional ) ,
104106 } ;
105107}
106108
107- const boolean = ( config ?: { optional : boolean } ) : BooleanValidator => ( {
108- type : 'boolean' ,
109- optional : Boolean ( config ?. optional ) ,
110- } ) ;
109+ function boolean ( config : { optional : true } ) : O < BooleanOptions > ;
110+ function boolean ( config : { optional : false } ) : R < BooleanOptions > ;
111+ function boolean ( ) : R < BooleanOptions > ;
112+ function boolean ( config ?: { optional : boolean } ) : BooleanValidator {
113+ return {
114+ type : 'boolean' ,
115+ optional : Boolean ( config ?. optional ) ,
116+ } ;
117+ }
111118
112- const list = < T extends Validator [ ] > (
113- list : T ,
114- config ?: { optional : boolean }
115- ) : ListValidator < T > => ( { type : 'list' , shape : list , optional : Boolean ( config ?. optional ) } ) ;
119+ function list < T extends Validator [ ] > ( list : T ) : R < ListOptions < T > > ;
120+ function list < T extends Validator [ ] > ( list : T , config : { optional : false } ) : R < ListOptions < T > > ;
121+ function list < T extends Validator [ ] > ( list : T , config : { optional : true } ) : O < ListOptions < T > > ;
122+ function list < T extends Validator [ ] > ( list : T , config ?: { optional : boolean } ) : ListValidator < T > {
123+ return { type : 'list' , shape : list , optional : Boolean ( config ?. optional ) } ;
124+ }
116125
117- const listof = < T extends Validator > ( v : T , config ?: { optional : boolean } ) : ListofValidator < T > => ( {
118- type : 'listof' ,
119- of : v ,
120- optional : Boolean ( config ?. optional ) ,
121- } ) ;
126+ function listof < T extends Validator > ( v : T ) : R < ListofOptions < T > > ;
127+ function listof < T extends Validator > ( v : T , config : { optional : false } ) : R < ListofOptions < T > > ;
128+ function listof < T extends Validator > ( v : T , config : { optional : true } ) : O < ListofOptions < T > > ;
129+ function listof < T extends Validator > ( v : T , config ?: { optional : boolean } ) : ListofValidator < T > {
130+ return {
131+ type : 'listof' ,
132+ of : v ,
133+ optional : Boolean ( config ?. optional ) ,
134+ } ;
135+ }
122136
123- const record = < T extends Schema > ( s : T , config ?: { optional : boolean } ) : RecordValidator < T > => ( {
124- type : 'record' ,
125- shape : s ,
126- optional : Boolean ( config ?. optional ) ,
127- } ) ;
137+ function record < T extends Schema > ( s : T ) : R < RecordOptions < T > > ;
138+ function record < T extends Schema > ( s : T , config : { optional : false } ) : R < RecordOptions < T > > ;
139+ function record < T extends Schema > ( s : T , config : { optional : true } ) : O < RecordOptions < T > > ;
140+ function record < T extends Schema > ( s : T , config ?: { optional : boolean } ) : RecordValidator < T > {
141+ return {
142+ type : 'record' ,
143+ shape : s ,
144+ optional : Boolean ( config ?. optional ) ,
145+ } ;
146+ }
128147
129- const recordof = < T extends Validator > (
130- v : T ,
131- config ?: { optional : boolean }
132- ) : RecordofValidator < T > => ( { type : 'recordof' , of : v , optional : Boolean ( config ?. optional ) } ) ;
148+ function recordof < T extends Validator > ( v : T ) : R < RecordofOptions < T > > ;
149+ function recordof < T extends Validator > ( v : T , config : { optional : false } ) : R < RecordofOptions < T > > ;
150+ function recordof < T extends Validator > ( v : T , config : { optional : true } ) : O < RecordofOptions < T > > ;
151+ function recordof < T extends Validator > ( v : T , config ?: { optional : boolean } ) : RecordofValidator < T > {
152+ return {
153+ type : 'recordof' ,
154+ of : v ,
155+ optional : Boolean ( config ?. optional ) ,
156+ } ;
157+ }
133158
134159/* TEST USE-CASE */
135160const create = < S extends Schema > ( _ : S ) : DataFrom < S > => ( { } as DataFrom < S > ) ; // mock
136161const res = create ( {
137162 id : string ( { optional : true , maxLength : [ 10 , 'lksjdf' ] } ) ,
138163 name : string ( { maxLength : [ 20 , 'lkasjdf' ] } ) ,
139164 age : number ( ) ,
140- tags : listof ( string ( ) ) ,
165+ isAdult : boolean ( { optional : true } ) ,
166+ tags : listof ( string ( ) , { optional : true } ) ,
141167 stuff : list ( [ string ( ) , number ( ) , string ( ) ] ) ,
168+ other_stuff : listof ( record ( { x : number ( ) , y : number ( ) } ) ) ,
142169 friends : recordof ( record ( { name : string ( ) , age : number ( ) } ) ) ,
143170 meta : record ( {
144171 created : number ( ) ,
0 commit comments