Skip to content

Commit f922048

Browse files
committed
fix: fix validator circular refernce
1 parent e91e1ac commit f922048

1 file changed

Lines changed: 83 additions & 56 deletions

File tree

src/t.ts

Lines changed: 83 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,61 @@
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

55
interface StringOptions {
66
type: 'string';
77
maxLength?: [number, string];
88
minLength?: [number, string];
99
pattern?: [RegExp, string];
1010
}
11-
type StringValidator = V<StringOptions>;
1211

1312
interface 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+
5051
type 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

5960
interface 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

6970
type 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>;
8991
function 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>;
100102
function 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 */
135160
const create = <S extends Schema>(_: S): DataFrom<S> => ({} as DataFrom<S>); // mock
136161
const 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

Comments
 (0)