Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- const log = (a: any) => console.log(JSON.stringify(a, null, 2));
- // implementation
- interface RecordErrors {
- [k: string]: Array<string | RecordErrors>;
- }
- type Errors = Array<string | RecordErrors>;
- type Validator<From, To> = (value: From) => Check<To>;
- type RecordValidator<T extends {}> = {
- [K in keyof T]: Validator<unknown, T[K]>
- };
- type ValidateRecord<T extends {}> = Check<T>;
- type ValidatorType<
- T extends (value: unknown) => ValidateRecord<any>
- > = T extends (value: unknown) => ValidateRecord<infer U> ? U : never;
- type Check<T> = { value: T; errors?: undefined } | { errors: Errors };
- function validateRecord<T extends {}>(
- record: RecordValidator<T>,
- ): (value: unknown) => ValidateRecord<T> {
- return value => {
- const output: Partial<T> = {};
- if (typeof value !== 'object') {
- return { errors: [`Unexpected ${typeof value}`] };
- }
- if (value === null) {
- return { errors: ['Unexpected null'] };
- }
- const errors: RecordErrors = {};
- for (const k of Object.keys(record) as Array<keyof T>) {
- const field = value[k as keyof typeof value];
- const result = record[k](field);
- if (result.errors) {
- errors[k as string] = (errors[k as string] || []).concat(result.errors);
- continue;
- }
- output[k] = result.value !== undefined ? result.value : field;
- }
- if (Object.keys(errors).length > 0) {
- return { errors: [errors] };
- }
- return { value: output as T };
- };
- }
- function compose<To, From = unknown>(
- typecheck: Validator<From, To>,
- ...passes: Array<Validator<To, To>>
- ): (value: From) => Check<To> {
- return (value: From): Check<To> => {
- const first = typecheck(value);
- if (first.errors) return first;
- let step: To = first.value !== undefined ? first.value : (value as any);
- for (const validate of passes) {
- const result = validate(step);
- if (result.errors) return result;
- step = result.value !== undefined ? result.value : step;
- }
- return { value: step };
- };
- }
- // validators
- const isString = (value: unknown): Check<string> =>
- typeof value === 'string' ? { value } : { errors: ['is not a string'] };
- const isBoolean = (value: unknown): Check<boolean> =>
- typeof value === 'boolean' ? { value } : { errors: ['is not a boolean'] };
- const isNumber = (value: unknown): Check<number> =>
- typeof value === 'number' ? { value } : { errors: ['is not a number'] };
- const isArray = <T>(check: (value: unknown) => Check<T>) => (
- values: unknown,
- ): Check<T[]> => {
- if (!Array.isArray(values)) {
- return { errors: ['is not an array'] };
- }
- const results = values.map(check);
- const error = results.filter(({ errors }) => errors)[0];
- if (error && error.errors) {
- return { errors: error.errors };
- }
- const successes = results as Array<{ value: T }>;
- return {
- value: successes.map(result => result.value),
- };
- };
- const minLength = <T extends { length: number }>(length: number) => (
- value: T,
- ): Check<T> =>
- value.length >= length
- ? { value }
- : { errors: [`expected length to be greater or equal to ${length}`] };
- const inRange = (from: number, to: number = Infinity) => (
- value: number,
- ): Check<number> =>
- value >= from && value < to
- ? { value }
- : { errors: [`expected to be inside of ${from}..${to} range`] };
- // transformers
- const capitalize = (value: string): Check<string> => ({
- value: value.charAt(0).toUpperCase() + value.slice(1),
- });
- // usage
- const validateOrder = validateRecord({
- title: isString,
- });
- const validateName = compose(
- isString,
- capitalize,
- );
- const validateUser = validateRecord({
- id: isString,
- name: validateName,
- password: compose(
- isString,
- minLength(6),
- ),
- active: isBoolean,
- age: compose(
- isNumber,
- inRange(1, 200),
- ),
- order: validateOrder,
- orders: isArray<Order>(validateOrder),
- orders1: compose<Order[]>(
- isArray(validateOrder),
- minLength(1),
- ),
- });
- type Order = ValidatorType<typeof validateOrder>;
- type User = ValidatorType<typeof validateUser>;
- type a = User['orders1'];
- log(
- validateUser({
- id: '1',
- name: 'nome',
- password: '123456',
- active: true,
- age: 10,
- order: { title: 'Order Title' },
- orders: [],
- orders1: [{ title: 'Order Title' }],
- }),
- );
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement