Skip to content

Commit

Permalink
refactor: refactor type-utils & DRY shaped and of validator
Browse files Browse the repository at this point in the history
  • Loading branch information
5alidz committed Aug 28, 2021
1 parent 5c954f4 commit 10b088b
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 73 deletions.
112 changes: 44 additions & 68 deletions src/createErrors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
ObjectKeys,
toObj,
shouldAddToResult,
isArray,
} from './utils';
import {
BooleanValidator,
Expand All @@ -20,6 +21,7 @@ import {
UnionValidator,
Validator,
} from './validatorTypes';
import { InferResult, InferCallbackResult } from './type-utils';
import { TYPEERR } from './constants';
import invariant from 'tiny-invariant';

Expand All @@ -37,26 +39,42 @@ function enterNode(validator: Validator, value: unknown, eager = false) {
return fn(validator, value, eager);
}

type InferCallbackResult<V extends Validator> = V extends
| StringValidator
| NumberValidator
| BooleanValidator
| ConstantValidator<any>
| UnionValidator<any>
? string
: V extends ListValidator<infer U>
? { [key in number]?: InferCallbackResult<U[key]> }
: V extends ListofValidator<infer U>
? { [key: number]: InferCallbackResult<U> | undefined }
: V extends RecordValidator<infer U>
? { [key in keyof U]?: InferCallbackResult<U[key]> }
: V extends RecordofValidator<infer U>
? { [key: string]: InferCallbackResult<U> | undefined }
: never;

type InferResult<S extends Schema> = {
[key in keyof S]?: InferCallbackResult<S[key]>;
};
function parseShapeValidator(
validator: RecordValidator<any> | ListValidator<any[]>,
value: unknown,
eager = false
) {
const shape = toObj(validator).shape;
const keys = ObjectKeys(shape);
const values = toObj(value);
const result: Record<string, any> = {};
for (let i = 0; i < keys.length; i++) {
const currentResult = enterNode(shape[keys[i]], values[keys[i]]);
if (shouldAddToResult(currentResult)) {
result[keys[i]] = currentResult;
if (eager) return result;
}
}
return normalizeResult(result);
}

function parseOfValidator(
validator: RecordofValidator<any> | ListofValidator<any>,
value: unknown,
eager = false
) {
const values = toObj(value);
const keys = ObjectKeys(values);
const result: Record<string, any> = {};
for (let i = 0; i < keys.length; i++) {
const currentResult = enterNode(validator.of, values[keys[i]]);
if (shouldAddToResult(currentResult)) {
result[keys[i]] = currentResult;
if (eager) return result;
}
}
return normalizeResult(result);
}

const validators = {
string(validator: StringValidator, value: unknown) {
Expand Down Expand Up @@ -117,65 +135,23 @@ const validators = {
},
list<T extends Validator[]>(validator: ListValidator<T>, value: unknown, eager = false) {
if (shouldSkipValidation(value, validator)) return null;
if (!Array.isArray(value)) return TYPEERR;
const shape = toObj(validator).shape;
const keys = ObjectKeys(shape);
const values = toObj(value);
const result: Record<string, any> = {};
for (let i = 0; i < keys.length; i++) {
const currentResult = enterNode(shape[keys[i]], values[keys[i]]);
if (shouldAddToResult(currentResult)) {
result[keys[i]] = currentResult;
if (eager) return result;
}
}
return normalizeResult(result);
if (!isArray(value)) return TYPEERR;
return parseShapeValidator(validator, value, eager);
},
listof<T extends Validator>(validator: ListofValidator<T>, value: unknown, eager = false) {
if (shouldSkipValidation(value, validator)) return null;
if (!Array.isArray(value)) return TYPEERR;
const values = toObj(value);
const keys = ObjectKeys(values);
const result: Record<string, any> = {};
for (let i = 0; i < keys.length; i++) {
const currentResult = enterNode(validator.of, values[keys[i]]);
if (shouldAddToResult(currentResult)) {
result[keys[i]] = currentResult;
if (eager) return result;
}
}
return normalizeResult(result);
if (!isArray(value)) return TYPEERR;
return parseOfValidator(validator, value, eager);
},
record<T extends Schema>(validator: RecordValidator<T>, value: unknown, eager = false) {
if (shouldSkipValidation(value, validator)) return null;
if (!isPlainObject(value)) return TYPEERR;
const shape = toObj(validator).shape;
const keys = ObjectKeys(shape);
const values = toObj(value);
const result: Record<string, any> = {};
for (let i = 0; i < keys.length; i++) {
const currentResult = enterNode(shape[keys[i]], values[keys[i]]);
if (shouldAddToResult(currentResult)) {
result[keys[i]] = currentResult;
if (eager) return result;
}
}
return normalizeResult(result);
return parseShapeValidator(validator, value, eager);
},
recordof<T extends Validator>(validator: RecordofValidator<T>, value: unknown, eager = false) {
if (shouldSkipValidation(value, validator)) return null;
if (!isPlainObject(value)) return TYPEERR;
const values = toObj(value);
const keys = ObjectKeys(values);
const result: Record<string, any> = {};
for (let i = 0; i < keys.length; i++) {
const currentResult = enterNode(validator.of, values[keys[i]]);
if (shouldAddToResult(currentResult)) {
result[keys[i]] = currentResult;
if (eager) return result;
}
}
return normalizeResult(result);
return parseOfValidator(validator, value, eager);
},
};

Expand Down
22 changes: 22 additions & 0 deletions src/type-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
Schema,
ConstantValidator,
UnionValidator,
Validator,
} from './validatorTypes';

type InferTypeWithOptional<T, U> = T extends O<T> ? U | undefined : U;
Expand Down Expand Up @@ -43,3 +44,24 @@ type InferDataType<T> = T extends UnionValidator<infer U>
export type DataFrom<S extends Schema> = {
[K in keyof S]: InferDataType<S[K]>;
};

export type InferCallbackResult<V extends Validator> = V extends
| StringValidator
| NumberValidator
| BooleanValidator
| ConstantValidator<any>
| UnionValidator<any>
? string
: V extends ListValidator<infer U>
? { [key in number]?: InferCallbackResult<U[key]> }
: V extends ListofValidator<infer U>
? { [key: number]: InferCallbackResult<U> | undefined }
: V extends RecordValidator<infer U>
? { [key in keyof U]?: InferCallbackResult<U[key]> }
: V extends RecordofValidator<infer U>
? { [key: string]: InferCallbackResult<U> | undefined }
: never;

export type InferResult<S extends Schema> = {
[key in keyof S]?: InferCallbackResult<S[key]>;
};
7 changes: 2 additions & 5 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export const ObjectKeys = Object.keys.bind(Object);
export const isArray = (value: unknown): value is any[] => Array.isArray(value);
export const isBool = (value: unknown): value is boolean => typeof value == 'boolean';
export const isString = (value: unknown): value is string => typeof value == 'string';
export const isNumber = (value: unknown): value is number =>
Expand All @@ -24,9 +25,5 @@ export function shouldAddToResult(res: unknown) {
}

export function toObj(value: any) {
return Array.isArray(value)
? { ...value }
: isPlainObject(value)
? value
: ({} as Record<string, any>);
return isArray(value) ? { ...value } : isPlainObject(value) ? value : ({} as Record<string, any>);
}

0 comments on commit 10b088b

Please sign in to comment.