@vlr/validity
basic validation library
creating a validator
lets say you have a typescript type
export interface Person {
name: string;
address: string;
age: number;
foodTaste?: string;
}
And you want to decide if object is valid or not, based on following rules:
a. Name is required and can't be longer than 20 characters
b. Address is required.
c. Age is not negative and less than 100.
c. Food taste is not constrained to anything, not required.
To create such a validator:
import {Validator, ObjectValidator, required, maxLength, equalOrMoreThan, lessThan} from '@vlr/validity'
const personValidator: Validator<Person> = {
name: [required(), maxLength(20)],
address: required(),
age: [equalOrGreater(0), lessThan(100)]
};
validating an object
To validate an object:
import { validate } from '@vlr/validity';
function isMyPersonValid(somePerson: Person): boolean {
const result: Validation<Person> = validate(personValidator, somePerson);
return result._valid;
}
To get validation messages
const nameMessages = validate(personValidator, somePerson).name._messages;
overriding validation messages
By default, validation messages are supposed to be translated/localized, so basic message for "required" validator is "validation.required" You can override that message like this.
const personValidator: Validator<Person> = {
name: required('name is required'),
address: required('address is required'),
};
nesting objects
export interface Vehicle {
licencePlate: string;
driver: Person;
passenger: Person;
}
const vehicleValidator: Validator<Vehicle> = {
licencePlate: // some complex custom validation here,
driver: [required(), personValidator]
passenger: personValidator
};
Note: when passenger is null, then result.passenger._valid will be evaluated to true, otherwise it is calculated by validation rules.
nesting arrays
export interface Company {
title: string;
employees: Person[];
}
const companyValidator: Validator<Company> = {
title: required(),
employees: [required(), arrayValidator<Person>(personValidator)]
};
const firstEmployeeValid = result.employees[0]._valid;
creating custom validator
import { validator } from '@vlr/validity';
const licensePlateValidator = validator<string>(isLicensePlateValid, 'license plate is invalid');
function isLicensePlateValid(plate: string): boolean {
custom logic
}
const vehicleValidator: Validator<Vehicle> = {
licencePlate: licensePlateValidator
};
cross-field validation
For example, if you need to check that field of an object should be no higher than the other field, i.e. one field is editable and the other comes from DB. For such case you can create validator like this
import { validator } from '@vlr/validity';
interface MyType {
value: number;
limit: number;
}
const myTypeValidator: Validator<MyType> = {
value: validator((value, obj) => value <= obj.limit, 'value should not be higher than limit');
};
dynamically enabling-disabling validators
"enabledIf" can be used to enable/disable validators coming after it, useful if item is hidden from view this construct will not disable validators before it in the array.
import { validator } from '@vlr/validity';
interface MyType {
value: number;
limit: number;
isHidden: boolean;
}
const validators: ChildValidator<string, MyType> = [enabledIf(<>)]
const myTypeValidator: Validator<MyType> = {
value: [enabledIf((value, obj) => !obj.isHidden), required()]
};
"required" validator has its own disabling mechanism
const myTypeValidator: Validator<MyType> = {
// null here is a placeholder for a message
value: [required(null, (value, obj) => !obj.isHidden)]
};