Last active
May 25, 2021 13:54
-
-
Save Schniz/853b9c60cd1e27bfc11d453ec3b9d091 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
type Validator<Input, Error> = (input: Input) => Error[]; | |
type ExtractValidatorError<V extends Validator<any, any>> = V extends Validator<any, infer A> ? A : never; | |
function error<Error>(error: Error): Error[] { | |
return [error]; | |
} | |
function ok(): any[] { | |
return []; | |
} | |
function tryCatch<Input, Error>(fn: (input: Input) => void, onError: (e: unknown) => Error): Validator<Input, Error> { | |
return input => { | |
try { | |
fn(input); | |
return ok(); | |
} catch (e) { | |
return error(onError(e)); | |
} | |
} | |
} | |
function validations<Input>(): <Validators extends Array<Validator<Input, any>>>(...validations: Validators) => Validator<Input, ExtractValidatorError<Validators[number]>> { | |
return function consumeValidators<Validators extends Array<Validator<Input, any>>>(...validations: Validators) { | |
type Error = ExtractValidatorError<Validators[number]>; | |
return composeValidations<Input, Error>(...validations); | |
} | |
} | |
function composeValidations<Input, Error>(...args: Validator<Input, Error>[]): Validator<Input, Error> { | |
return input => { | |
const errors: Error[] = []; | |
for (const validator of args) { | |
errors.push(...validator(input)); | |
} | |
return errors; | |
} | |
} | |
////////////////// USAGE ////////////////// | |
const user = { | |
/** a name */ | |
name: "Gal Schlezinger", | |
/** an age */ | |
age: 29, | |
/** an occupation */ | |
occupation: "Developer", | |
type: "human" as const, | |
}; | |
type User = typeof user; | |
function mustBe18(user: Pick<User, 'age'>): string[] { | |
if (user.age < 18) { | |
return error(`User age must be above 18. Got: ${user.age}`); | |
} | |
return ok(); | |
} | |
function goatOrHuman(user: { type: "human" } | { type: "goat" }): string[] { | |
return []; | |
} | |
const nameMustBePreset = tryCatch((input: Pick<User, 'name'>) => { | |
if (input.name.trim().length === 0) { | |
throw new Error("Name must be present"); | |
} | |
}, e => (e as Error).message) | |
const validator = validations<User>()(mustBe18, nameMustBePreset, goatOrHuman); | |
function validate(input: Parameters<typeof validator>[0]) { | |
console.log({ input, output: validator(input) }); | |
} | |
validate(user); | |
validate({ | |
age: 17, | |
name: "", | |
occupation: "", | |
type: "human", | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment