Skip to content

Instantly share code, notes, and snippets.

@Radiergummi
Created August 11, 2021 05:54
Show Gist options
  • Save Radiergummi/bb31b7a74073359f9a835b4afed1d571 to your computer and use it in GitHub Desktop.
Save Radiergummi/bb31b7a74073359f9a835b4afed1d571 to your computer and use it in GitHub Desktop.
/**
* Validates a value that isn't falsy.
*
* @param field
* @param message
*/
export function required(
field?: string,
message?: string | Message<string>,
): Validator {
return validator(
( v?: string ) => !!v,
msg( field, message || `${ field } is required` ),
);
}
/**
* Validates a string is an email address. Please note that this validator is
* exceptionally forgiving by merely checking whether the value consists of
* "something", followed by an @ sign, followed by "something" dot "something".
* As validating email addresses is by and large pointless, this should be
* enough to ensure we receive something that mail can be sent to.
*
* @param field Name of the field under validation.
* @param message Message to show in case of an invalid value.
* @see https://stackoverflow.com/questions/46155 for more discussion.
*/
export function email(
field?: string,
message?: string | Message<string>,
): Validator<string> {
return matches( /\S+@\S+\.\S+/, field, message || (
() => `${ field } must be a valid email address`
) );
}
export function website(
field?: string,
message?: string | Message<string>,
): Validator<string> {
return matches(
/^((http|https):\/\/)?(www.)?(?!.*(ftp|http|https|www.))[a-zA-Z0-9_-]+(\.[a-zA-Z]+)+((\/)[\w#]+)*(\/\w+\?[a-zA-Z0-9_]+=\w+(&[a-zA-Z0-9_]+=\w+)*)?$/,
field,
message || (
() => `${ field } must be a valid website`
),
);
}
/**
* Validates a string is shorter than a given amount of characters.
*
* @param expression Regular expression to match.
* @param field Name of the field under validation.
* @param message Message to show in case of an invalid value.
*/
export function matches(
expression: string | RegExp | ValueFn<string | RegExp>,
field?: string,
message?: string | Message<string>,
): Validator<string> {
function unwrap( wrapped: string | RegExp | ValueFn<string | RegExp> ): RegExp {
const value = val( wrapped );
return typeof value === 'string'
? new RegExp( value )
: value;
}
return validator<string>(
( v?: string ) => (
!!v &&
unwrap( expression ).exec( v ) !== null
),
msg( field, message ),
);
}
/**
* Validates a string is shorter than a given amount of characters.
*
* @param characterAmount Maximum allowed number of characters.
* @param field Name of the field under validation.
* @param message Message to show in case of an invalid value.
*/
export function shorterThan(
characterAmount: number | ValueFn<number>,
field?: string,
message?: string | Message<string>,
): Validator {
return validator(
( v?: string ) => (
!v || (
!!v && val<number>( characterAmount ) > v.length
)
),
msg(
field,
message || ( () => `${ field } must be shorter than ${ val<number>( characterAmount ) } characters.` ),
),
);
}
/**
* Validates a string is longer than a given amount of characters.
*
* @param characterAmount Minimum required number of characters.
* @param field Name of the field under validation.
* @param message Message to show in case of an invalid value.
*/
export function longerThan(
characterAmount: number | ValueFn<number>,
field?: string,
message?: string | Message<string>,
): Validator {
return validator(
( v?: string ) => (
!!v &&
v.length > val<number>( characterAmount )
),
msg(
field,
message || ( () => `${ field } must be longer than ${ val<number>( characterAmount ) } characters.` ),
),
);
}
/**
* Validates a string equals another string.
*
* @param value Value to compare against.
* @param field Name of the field under validation.
* @param message Message to show in case of an invalid value.
*/
export function stringEquals(
value: string | ( () => string ),
field?: string,
message?: string | Message<string>,
): Validator {
return equals<string>(
value,
field,
message,
);
}
/**
* Validates a string equals another string if both are converted to lowercase
* characters only.
*
* @param value String to compare against.
* @param field Name of the field under validation.
* @param message Message to show in case of an invalid value.
*/
export function lowerStringEquals(
value: string | ( () => string ),
field?: string,
message?: string | Message<string>,
): Validator {
return validator(
( v?: string ) => (
!!v &&
val<string>( value ).toLowerCase() === v.toLowerCase()
),
msg( field, message ),
);
}
export function truthy(
field?: string,
message?: string | Message<string>,
): Validator {
return validator(
( v?: any ) => typeof v === 'string'
? [ 'on', 'true', 'yes' ].includes( v.toLowerCase() )
: !!v,
msg( field, message || `${ field } must be accepted` ),
);
}
/**
* Validates a value equals another value of a given type.
*
* @param value Value to compare against.
* @param field Name of the field under validation.
* @param message Message to show in case of an invalid value.
*/
export function equals<T>(
value: T | ValueFn<T>,
field?: string,
message?: string | Message<T>,
): Validator<T> {
return validator<T>(
( v?: T ): boolean => (
v === val<T>( value )
),
msg<T>( field, message ),
);
}
/**
* Curries a validator function.
*
* @param predicate Predicate function to evaluate the value against.
* @param message Message function to generate an error function if
* validation fails.
* @return Validator<T> Validator function for the given predicate and message.
*/
export function validator<T>(
predicate: Predicate<T>,
message: Message<T>,
): Validator<T> {
return ( v?: T ) => predicate( v ) || message( v ).trim();
}
/**
* Evaluates value functions to comparison values or uses plain values otherwise
* if given. Both value function results and values must be of type T.
*
* @param value
* @return T
*/
function val<T = any>( value: T | ValueFn<T> ): T {
return typeof value === 'function'
? ( value as ValueFn<T> )()
: value;
}
/**
* Converts plain string messages or missing values to a valid message function
* for a given value type T.
*
* @param field Name of the field under validation.
* @param message Given message handler as passed to the validator.
* @return Message<T> Valid message function.
*/
function msg<T>( field?: string, message?: string | Message<T> ): Message<T> {
if ( !message ) {
return (): string => `${ field || 'Field' } is invalid`;
}
return typeof message === 'string'
? () => message
: message;
}
/**
* A value function for a given value type T that returns a value of type T.
* This may be used to pass dynamic comparison expressions to validators that
* should be evaluated at validation time instead of validator creation time.
*/
type ValueFn<T> = () => T;
/**
* A predicate function for a given value type T that assesses whether a value
* is valid. If the value under validation has not been provided, the parameter
* will be undefined. The function returns a boolean true to indicate a valid
* value, or a boolean false to indicate an invalid value.
*/
export type Predicate<T> = ( value: T | undefined ) => boolean;
/**
* A message function to generate an error message for a given invalid value of
* type T. It receives the field name and invalid value, if available, as its
* parameters, and returns an error message.
*/
export type Message<T> = ( value: T | undefined ) => string;
/**
* A validator function for a given value type T. It either returns true, if the
* validator assessed a given value as valid, or a string message in case of an
* invalid value.
*/
export type Validator<T = any> = ( v?: T ) => true | string;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment