Skip to content

Commit 0ac317e

Browse files
committed
refactor: traverse types & seperate tests
1 parent a827591 commit 0ac317e

10 files changed

Lines changed: 618 additions & 567 deletions

.eslintignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
dist/

src/createErrors.ts

Lines changed: 61 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,79 @@
11
import { traverse } from './traverse';
2-
import { isPlainObject, ObjectKeys, isNumber, isString, isBool } from './utils';
3-
import { Validator } from './validatorTypes';
2+
import { isPlainObject, isNumber, isString, isBool } from './utils';
3+
import { Schema, Validator } from './validatorTypes';
44
import { TYPEERR } from './constants';
55

66
function shouldSkipValidation(value: unknown, validator: Validator) {
77
return value == null && Boolean(validator.optional);
88
}
99

10-
export function createErrors<T>(schema: { [K in keyof T]: Validator }, data: T, eager = false) {
11-
if (!isPlainObject(data)) {
12-
return (ObjectKeys(schema) as (keyof T)[]).reduce((errors, schemaKey) => {
13-
const validator = schema[schemaKey];
14-
if (!validator.optional) {
15-
errors[schemaKey] = TYPEERR as string;
16-
}
17-
return errors;
18-
}, {} as Record<keyof T, any>);
19-
} else {
20-
return traverse(
21-
schema,
22-
{
23-
string(__, _, validator, value) {
24-
if (shouldSkipValidation(value, validator)) return null;
25-
if (!isString(value)) return TYPEERR;
10+
export function createErrors<T extends Schema>(schema: T, data: any, eager = false) {
11+
return traverse(
12+
schema,
13+
{
14+
string: ({ validator, value }) => {
15+
if (shouldSkipValidation(value, validator)) return null;
16+
if (!isString(value)) return TYPEERR;
2617

27-
const [minLength, minLengthErrMsg] = validator.minLength ? validator.minLength : [];
28-
if (minLength && minLengthErrMsg && isNumber(minLength) && value.length < minLength)
29-
return minLengthErrMsg;
18+
const [minLength, minLengthErrMsg] = validator.minLength ? validator.minLength : [];
19+
if (minLength && minLengthErrMsg && isNumber(minLength) && value.length < minLength)
20+
return minLengthErrMsg;
3021

31-
const [maxLength, maxLengthErrMsg] = validator.maxLength ? validator.maxLength : [];
32-
if (maxLength && maxLengthErrMsg && isNumber(maxLength) && value.length > maxLength)
33-
return maxLengthErrMsg;
22+
const [maxLength, maxLengthErrMsg] = validator.maxLength ? validator.maxLength : [];
23+
if (maxLength && maxLengthErrMsg && isNumber(maxLength) && value.length > maxLength)
24+
return maxLengthErrMsg;
3425

35-
const [pattern, patterErrMsg] = validator.pattern ? validator.pattern : [];
36-
if (pattern && patterErrMsg && pattern.test(value) == false) return patterErrMsg;
26+
const [pattern, patterErrMsg] = validator.pattern ? validator.pattern : [];
27+
if (pattern && patterErrMsg && pattern.test(value) == false) return patterErrMsg;
3728

38-
return null;
39-
},
40-
number(__, _, validator, value) {
41-
if (shouldSkipValidation(value, validator)) return null;
29+
return null;
30+
},
31+
number: ({ validator, value }) => {
32+
if (shouldSkipValidation(value, validator)) return null;
4233

43-
if (!isNumber(value)) return TYPEERR;
34+
if (!isNumber(value)) return TYPEERR;
4435

45-
const [min, minErrMsg] = validator.min ? validator.min : [];
46-
if (isNumber(min) && value < min && minErrMsg) return minErrMsg;
36+
const [min, minErrMsg] = validator.min ? validator.min : [];
37+
if (isNumber(min) && value < min && minErrMsg) return minErrMsg;
4738

48-
const [max, maxErrMsg] = validator.max ? validator.max : [];
49-
if (isNumber(max) && value > max && maxErrMsg) return maxErrMsg;
39+
const [max, maxErrMsg] = validator.max ? validator.max : [];
40+
if (isNumber(max) && value > max && maxErrMsg) return maxErrMsg;
5041

51-
const [is, isErrMsg] = validator.is ? validator.is : [];
52-
if (isString(is) && isErrMsg) {
53-
const isInt = Number.isInteger(value);
54-
if ((isInt && is == 'float') || (!isInt && is == 'integer')) return isErrMsg;
55-
}
42+
const [is, isErrMsg] = validator.is ? validator.is : [];
43+
if (isString(is) && isErrMsg) {
44+
const isInt = Number.isInteger(value);
45+
if ((isInt && is == 'float') || (!isInt && is == 'integer')) return isErrMsg;
46+
}
5647

57-
return null;
58-
},
59-
boolean(__, _, validator, value) {
60-
if (shouldSkipValidation(value, validator)) return null;
61-
if (!isBool(value)) return TYPEERR;
62-
return null;
63-
},
64-
list(__, _, validator, value) {
65-
if (shouldSkipValidation(value, validator)) return null;
66-
if (!Array.isArray(value)) return TYPEERR;
67-
return null;
68-
},
69-
listof(__, _, validator, value) {
70-
if (shouldSkipValidation(value, validator)) return null;
71-
if (!Array.isArray(value)) return TYPEERR;
72-
return null;
73-
},
74-
record(__, _, validator, value) {
75-
if (shouldSkipValidation(value, validator)) return null;
76-
if (!isPlainObject(value)) return TYPEERR;
77-
return null;
78-
},
79-
recordof(__, _, validator, value) {
80-
if (shouldSkipValidation(value, validator)) return null;
81-
if (!isPlainObject(value)) return TYPEERR;
82-
return null;
83-
},
48+
return null;
49+
},
50+
boolean: ({ validator, value }) => {
51+
if (shouldSkipValidation(value, validator)) return null;
52+
if (!isBool(value)) return TYPEERR;
53+
return null;
54+
},
55+
list: ({ validator, value }) => {
56+
if (shouldSkipValidation(value, validator)) return null;
57+
if (!Array.isArray(value)) return TYPEERR;
58+
return null;
59+
},
60+
listof: ({ validator, value }) => {
61+
if (shouldSkipValidation(value, validator)) return null;
62+
if (!Array.isArray(value)) return TYPEERR;
63+
return null;
64+
},
65+
record: ({ validator, value }) => {
66+
if (shouldSkipValidation(value, validator)) return null;
67+
if (!isPlainObject(value)) return TYPEERR;
68+
return null;
69+
},
70+
recordof: ({ validator, value }) => {
71+
if (shouldSkipValidation(value, validator)) return null;
72+
if (!isPlainObject(value)) return TYPEERR;
73+
return null;
8474
},
85-
data,
86-
eager
87-
);
88-
}
75+
},
76+
data,
77+
eager
78+
);
8979
}

src/createSchema.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,34 +3,34 @@ import { createErrors } from './createErrors';
33
import { DATAERR, $record, SCHEMAERR } from './constants';
44
import invariant from 'tiny-invariant';
55
import { RecordValidator, Schema } from './validatorTypes';
6-
import { DataFrom, ErrorsFrom } from './type-utils';
6+
import { DataFrom } from './type-utils';
77
import { traverse as _traverse, Visitor } from './traverse';
88

99
export function createSchema<T extends Schema>(_schema: T) {
1010
invariant(isPlainObject(_schema), SCHEMAERR);
11+
type Data = DataFrom<T>;
1112
const schema = Object.freeze({ ..._schema });
1213

1314
function validate(data: any, eager = false) {
1415
const errors = createErrors(schema, data, eager);
15-
return ObjectKeys(errors).length > 0 ? (errors as ErrorsFrom<DataFrom<T>>) : null;
16+
return ObjectKeys(errors).length > 0 ? errors : null;
1617
}
1718

18-
function is(data: any): data is DataFrom<T> {
19-
const errors = validate(data, true);
20-
return !errors && isPlainObject(data);
19+
function is(data: any): data is Data {
20+
return !validate(data, true) && isPlainObject(data);
2121
}
2222

2323
function embed(config = { optional: false }): RecordValidator<T> {
2424
return { type: $record, shape: schema, ...config };
2525
}
2626

27-
function produce(data: any): DataFrom<T> {
27+
function produce(data: any): Data {
2828
invariant(is(data), DATAERR);
2929
return data;
3030
}
3131

32-
function traverse(visitor: Visitor, data?: any, eager?: boolean) {
33-
return _traverse(schema, visitor, data, eager);
32+
function traverse<V extends Visitor>(visitor: V, data?: any, eager?: boolean) {
33+
return _traverse(schema as T, visitor, data, eager);
3434
}
3535

3636
return {

src/helpers.ts

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
RecordofOptions,
1919
StringOptions,
2020
} from './validatorTypes';
21+
import { $boolean, $list, $listof, $number, $record, $recordof, $string } from './constants';
2122

2223
export function string(config: { optional: true } & Omit<StringOptions, 'type'>): O<StringOptions>;
2324
export function string(config: { optional: false } & Omit<StringOptions, 'type'>): R<StringOptions>;
@@ -30,8 +31,8 @@ export function string(
3031
config?: { optional?: boolean } & Omit<StringOptions, 'type'>
3132
): StringValidator {
3233
return {
33-
type: 'string',
34-
optional: Boolean(config?.optional),
34+
type: $string,
35+
optional: !!config?.optional,
3536
...config,
3637
};
3738
}
@@ -47,8 +48,8 @@ export function number(
4748
config?: { optional?: boolean } & Omit<NumberOptions, 'type'>
4849
): NumberValidator {
4950
return {
50-
type: 'number',
51-
optional: Boolean(config?.optional),
51+
type: $number,
52+
optional: !!config?.optional,
5253
...config,
5354
};
5455
}
@@ -58,8 +59,8 @@ export function boolean(config: { optional: false }): R<BooleanOptions>;
5859
export function boolean(): R<BooleanOptions>;
5960
export function boolean(config?: { optional: boolean }): BooleanValidator {
6061
return {
61-
type: 'boolean',
62-
optional: Boolean(config?.optional),
62+
type: $boolean,
63+
optional: !!config?.optional,
6364
};
6465
}
6566

@@ -73,7 +74,7 @@ export function list<T extends Validator[]>(
7374
list: T,
7475
config?: { optional: boolean }
7576
): ListValidator<T> {
76-
return { type: 'list', optional: Boolean(config?.optional), shape: list };
77+
return { type: $list, optional: !!config?.optional, shape: list };
7778
}
7879

7980
export function listof<T extends Validator>(v: T): R<ListofOptions<T>>;
@@ -84,8 +85,8 @@ export function listof<T extends Validator>(
8485
config?: { optional: boolean }
8586
): ListofValidator<T> {
8687
return {
87-
type: 'listof',
88-
optional: Boolean(config?.optional),
88+
type: $listof,
89+
optional: !!config?.optional,
8990
of: v,
9091
};
9192
}
@@ -95,8 +96,8 @@ export function record<T extends Schema>(s: T, config: { optional: false }): R<R
9596
export function record<T extends Schema>(s: T, config: { optional: true }): O<RecordOptions<T>>;
9697
export function record<T extends Schema>(s: T, config?: { optional: boolean }): RecordValidator<T> {
9798
return {
98-
type: 'record',
99-
optional: Boolean(config?.optional),
99+
type: $record,
100+
optional: !!config?.optional,
100101
shape: s,
101102
};
102103
}
@@ -115,8 +116,8 @@ export function recordof<T extends Validator>(
115116
config?: { optional: boolean }
116117
): RecordofValidator<T> {
117118
return {
118-
type: 'recordof',
119+
type: $recordof,
119120
of: v,
120-
optional: Boolean(config?.optional),
121+
optional: !!config?.optional,
121122
};
122123
}

0 commit comments

Comments
 (0)