Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@Schniz
Last active May 25, 2021 13:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Schniz/853b9c60cd1e27bfc11d453ec3b9d091 to your computer and use it in GitHub Desktop.
Save Schniz/853b9c60cd1e27bfc11d453ec3b9d091 to your computer and use it in GitHub Desktop.
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