tsfv: Typescript Fluent Validation Library
A Typescript-based validation library with an extensible, fluent API and human-readable error messages.
Similar to and somewhat inspired by v8n, but addresses some limitations of that library:
- Full Typescript support (v8n has no typings)
- Error messages describing why validation failed (in English but localizable)
- Easy to differentiate between null and undefined for optional values
- Fluent validation of object keys and values
Installation
npm install tsfv
Usage
Typical Usage
; ;tsfv .length1, 64 .pattern/+/ .orUndefined .checkwords, 'words';
Complex Object Validation and Validator Re-use
.keystsfv.everytsfv.length2, 8.sometsfv.exact'id' .valuestsfv.everytsfv.anyOftsfv.integer, tsfv.string;objv.check;objv.testAll.forEachconsole.logerr.message;// Expected object with keys array with every element length between 2 and 8 and array with some element value exactly equal to "id"// Expected object with values array with every element (integer or string) ;aoav.check;aoav.testAll.forEachconsole.logerr.message;// Expected array with every element object with properties ids (array with every element string and length between 1 and 10 and minimum length of 1)
Custom Predicates
;div3.check9;div3.check'9';div3.testAllMath.PI.forEachconsole.logerr.message;// Expected divisible by 3div3.testAllwords.forEachconsole.logerr.message;// Expected numeric// Expected divisible by 3
Extensions
New validation builder methods can be added to the API using Typescript Module Augmentation:
// uppercase.ts; // augment appropriate interface for type in fluent APIdeclare // augment Validation classdeclare // add method to Validation classValidation.prototype.uppercase = ;
Use the extension by importing its module, in addition to tsfv
:
;; tsfv.uppercase.check'ABC';tsfv.uppercase.testAll'abc'.forEachconsole.logerr.message;// Expected uppercase string
Localized Error Messages
The core library always returns error messages in English. However, validation errors and rules preserve enough information to localize error messages into other languages:
;; tsfv .length1, 64 .pattern/+/ .orUndefined .testAllnull, 'words' .mapconsole.logerrorAufDeutscherr;// Erwartet eine Länge zwischen 1 und 64 für "words"// Erwartet eine Zeichenfolge die zu /([A-Z]+\s*)+/ passt für "words"
API
The entire API is exposed via the default export of the library, generally imported as tsfv
.
Note that all of the methods used to configure a validator are chainable. The return types shown below are simplified and illustrative; refer to the Typescript definition files for actual return types.
Core Validation API
The following methods are used to perform validation once the validator object has been built by calling builder methods.
check
check(value: any, name?: string): void
Validates the given value and throws a ValidationError
if it is invalid.
An optional variable name can be provided that will be included in any error message.
Example:
tsfv.string.check'x'; // returnstsfv.not.string.check'x'; // throws ValidationError
test
test(value: any): boolean
Tests whether the given value is valid according to the rules checked by this validator.
Example:
tsfv.string.test'x'; // truetsfv.not.string.test'x'; // false
testAll
testAll(value: any, name?: string): ValidationError[]
Validates the given value and returns an array of ValidationError
containing any and all failing rules.
An optional variable name can be provided that will be included in any error message.
Example:
tsfv.numeric.predicatev % 3 === 0, 'divisible by 3'.testAllnull.forEachconsole.logerr.message;// Expected numeric// Expected divisible by 3
describe
describe(): string
Returns an English description of the rules checked by this validator.
Example:
tsfv.numeric.predicatev % 3 === 0, 'divisible by 3'.describe; // numeric and divisible by 3
Modifiers
Modifiers change the behavior of the immediately following rule. They are implemented as property getters, so they are not followed by parentheses.
not
readonly not: Omit<InvertedValidation<this>, 'not'>
The not
modifier inverts/negates the logic of the immediately following rule.
It also disables any type-narrowing of the return type of that rule, essentially causing it to return a
validator of type this
instead of a type-specific validator (such as NumericValidation
or StringValidation
).
Double inversion/negation is not allowed by the type system.
Example:
tsfv.string.test'x'; // truetsfv.not.string.test'x'; // false
Type Validation Builders
The following methods validate the general type of a value.
array
array(): ArrayValidation
Returns a new validator that checks that the validated value is an array.
Assuming the method call was not preceded by not
, the interface of the returned validator
will contain only validation builders that apply to arrays.
Example:
tsfv.array.everytsfv.positive.test; // true
boolean
boolean(): AnyValidation
Returns a new validator that checks that the validated value is a boolean value (true
or false
).
Example:
tsfv.boolean.testfalse; // truetsfv.boolean.test1; // false
integer
integer(): NumericValidation
Returns a new validator that checks that the validated value is an integer.
Assuming the method call was not preceded by not
, the interface of the returned validator
will contain only validation builders that apply to numbers.
Example:
tsfv.integer.test42; // truetsfv.integer.test11; // false
null
null(): AnyValidation
Returns a new validator that checks that the validated value is null
.
Example:
tsfv.null.testnull; // truetsfv.null.testundefined; // false
number
number(): NumericValidation
Returns a new validator that checks that the validated value is a number
(including NaN
).
Assuming the method call was not preceded by not
, the interface of the returned validator
will contain only validation builders that apply to numbers.
Example:
tsfv.number.test42; // truetsfv.number.testInfinity; // truetsfv.number.testNaN; // truetsfv.number.test'42'; // false
numeric
numeric(): NumericValidation
Returns a new validator that checks that the validated value is numeric (including numeric strings
and Number
but excluding NaN
).
Assuming the method call was not preceded by not
, the interface of the returned validator
will contain only validation builders that apply to numbers.
Example:
tsfv.numeric.test42; // truetsfv.numeric.testInfinity; // truetsfv.numeric.testNaN; // falsetsfv.numeric.test'42'; // true
object
object(): ObjectValidation
Returns a new validator that checks that the validated value is an object (not including arrays).
Assuming the method call was not preceded by not
, the interface of the returned validator
will contain only validation builders that apply to objects.
Example:
tsfv.object.test; // truetsfv.object.testnew Date; // truetsfv.object.test; // falsetsfv.object.test'42'; // false
string
string(): StringValidation
Returns a new validator that checks that the validated value is a string.
Assuming the method call was not preceded by not
, the interface of the returned validator
will contain only validation builders that apply to strings.
Example:
tsfv.string.test'42'; // truetsfv.string.test42; // false
undefined
undefined(): AnyValidation
Returns a new validator that checks that the validated value is undefined
.
Example:
tsfv.undefined.testundefined; // truetsfv.undefined.testnull; // false
Generic Validation Builders (AnyValidation)
The following methods apply to validating values of any type.
anyOf
anyOf(...validators: Validator[]): AnyValidation
Checks that at least one of the given validators passes, essentially providing an or
operator.
If no validators are provided, no values are considered valid.
Example:
tsfv.anyOftsfv.number, tsfv.string.test42; // truetsfv.anyOftsfv.number, tsfv.string.test'hello'; // truetsfv.anyOftsfv.number, tsfv.string.testnull; // false
equal
equal(value: any): AnyValidation
Returns a new validator that checks that the validated value loosely equals (using ==
) the given value.
Example:
tsfv.equalnull.testundefined; // truetsfv.equal42.test'42'; // truetsfv.equal'hello'.test; // truetsfv.equalNaN.testNaN; // false
exact
exact(value: any): AnyValidation
Returns a new validator that checks that the validated value strictly equals (using ===
) the given value.
Example:
tsfv.exactnull.testnull; // truetsfv.exactnull.testundefined; // falsetsfv.exact42.test42; // truetsfv.exact42.test'42'; // falsetsfv.exactNaN.testNaN; // false
predicate
predicate(test: Predicate, description: string): AnyValidation
Returns a new validator that checks that the validated value against the given predicate function. The given description string is used in error messages for invalid values.
Example:
tsfv.numeric.predicatev % 3 === 0, 'divisible by 3'.test9; // true
Numeric Validation Builders (NumericValidation)
The following methods apply to validating numeric values, which include number
, strings parsable as numbers, and Number
objects.
between
between(min: number, max: number): NumericValidation
Returns a new validator that checks that the validated value is numeric and within the given range, inclusive.
Throws an Error
if min > max
.
Example:
tsfv.between1, 10.test5; // truetsfv.between1, 10.test'5'; // truetsfv.between1, 10.test0; // false
greaterThan
greaterThan(bound: number): NumericValidation
Returns a new validator that checks that the validated value is numeric and greater than the given minimum.
Example:
tsfv.greaterThan1.test2; // truetsfv.greaterThan1.test'2'; // truetsfv.greaterThan1.test1; // false
greaterThanOrEqual
greaterThanOrEqual(bound: number): NumericValidation
Returns a new validator that checks that the validated value is numeric and greater than or equal to the given minimum.
Example:
tsfv.greaterThanOrEqual1.test2; // truetsfv.greaterThanOrEqual1.test'1'; // truetsfv.greaterThanOrEqual1.test0; // false
lessThan
lessThan(bound: number): NumericValidation
Returns a new validator that checks that the validated value is numeric and less than the given minimum.
Example:
tsfv.lessThan1.test0; // truetsfv.lessThan1.test'0'; // truetsfv.lessThan1.test1; // false
lessThanOrEqual
lessThanOrEqual(bound: number): NumericValidation
Returns a new validator that checks that the validated value is numeric and less than or equal to the given minimum.
Example:
tsfv.lessThanOrEqual1.test0; // truetsfv.lessThanOrEqual1.test'1'; // truetsfv.lessThanOrEqual1.test2; // false
positive
positive(): NumericValidation
Returns a new validator that checks that the validated value is numeric and positive (> 0
).
Example:
tsfv.positive.test1; // truetsfv.positive.test'1'; // truetsfv.positive.test0; // false
negative
negative(): NumericValidation
Returns a new validator that checks that the validated value is numeric and negative (< 0
).
Example:
tsfv.negative.test-1; // truetsfv.negative.test'-1'; // truetsfv.negative.test0; // false
Length Validation Builders (LengthValidation)
The following methods apply to validating the length of strings and arrays.
length
length(min: number, max?: number): LengthValidation
Checks that the length of the validated value is within the given range, inclusive.
If max
is omitted, it is assumed to equal min
.
Throws an Error
if min > max
.
Example:
tsfv.length1.test; // truetsfv.length1, 3.test; // falsetsfv.length1.test'a'; // truetsfv.length1, 3.test''; // false
minLength
minLength(min: number): LengthValidation
Checks that the length of the validated value is greater than or equal to the given minimum.
Example:
tsfv.minLength1.test; // truetsfv.minLength1.test; // falsetsfv.minLength1.test'a'; // truetsfv.minLength1.test''; // false
maxLength
maxLength(max: number): LengthValidation
Checks that the length of the validated value is less than or equal to the given maximum.
Example:
tsfv.maxLength1.test; // truetsfv.maxLength1.test; // falsetsfv.maxLength1.test'a'; // truetsfv.maxLength1.test'ab'; // false
empty
empty(): LengthValidation
Returns a new validator that checks that the validated value is an empty string or array.
Example:
tsfv.empty.test; // truetsfv.empty.test; // falsetsfv.empty.test''; // truetsfv.empty.test'a'; // false
String Validation Builders (StringValidation)
The following methods apply to validating strings.
pattern
pattern(regex: RegExp): StringValidation
Returns a new validator that checks that the validated value is a string matching the given regular expression.
Example:
tsfv.pattern/+/.test'HELLO WORLD'; // truetsfv.pattern/+/.test'hello world'; // false
contains
contains(str: string): StringValidation
Returns a new validator that checks that the validated value is a string containing the given substring.
Example:
tsfv.contains'ell'.test'hello'; // true
startsWith
startsWith(str: string): StringValidation
Returns a new validator that checks that the validated value is a string starting with the given substring.
Example:
tsfv.startsWith'hell'.test'hello'; // true
endsWith
endsWith(str: string): StringValidation
Returns a new validator that checks that the validated value is a string ending with the given substring.
Example:
tsfv.endsWith'ello'.test'hello'; // true
Array Validation Builders (ArrayValidation)
The following methods apply to validating arrays.
includes
includes(value: any): ArrayValidation
Returns a new validator that checks that the validated value is an array containing the given element.
Example:
tsfv.includes2.test; // true
every
every(elementValidator: Validator): ArrayValidation
Returns a new validator that checks that the validated value is an array for which every element passes the given validator.
Example:
tsfv.everytsfv.positive.test; // true
some
some(elementValidator: Validator): ArrayValidation
Returns a new validator that checks that the validated value is an array for which at least one element passes the given validator.
Example:
tsfv.sometsfv.positive.test; // true
Object Validation Builders (ObjectValidation)
The following methods apply to validating objects.
instanceOf
instanceOf(ctor: Function): ObjectValidation
Returns a new validator that checks that the validated value is an object that is an instance of the given class (or a subclass).
Example:
tsfv.instanceOfObject.test; // truetsfv.instanceOfFunction.test0; // true
keys
keys(arrayValidator: Validator): ObjectValidation
Returns a new validator that checks that the validated value is an object for which the keys pass the given validator.
Example:
tsfv.keystsfv.length1.test; // truetsfv.keystsfv.everytsfv.length1.test; // true
values
values(arrayValidator: Validator): ObjectValidation
Returns a new validator that checks that the validated value is an object for which the values pass the given validator.
Example:
tsfv.valuestsfv.everytsfv.numeric.test; // true
properties
properties<T>(propertyValidator: { [P in keyof T]: Validator }, only = false): ObjectValidation
Returns a new validator that checks that the validated value is an object with properties that pass the given validator.
If only
is true, additional, unvalidated properties will fail validation; otherwise, they are ignored.
Example:
tsfv.properties.test; // truetsfv.properties, true.test; // false
Optional Value Builders
The following methods build a validator that accepts null
and/or undefined
, in addition to valid values.
optional
optional(): this
Returns a new validator that allows null
or undefined
, in addition to valid values.
Example:
tsfv.string.optional.test'hello'; // truetsfv.string.optional.testnull; // truetsfv.string.optional.testundefined; // true
orNull
orNull(): this
Returns a new validator that allows null
, in addition to valid values.
Example:
tsfv.string.orNull.test'hello'; // truetsfv.string.orNull.testnull; // truetsfv.string.orNull.testundefined; // false
orUndefined
orUndefined(): this
Returns a new validator that allows undefined
, in addition to valid values.
Example:
tsfv.string.orUndefined.test'hello'; // truetsfv.string.orUndefined.testundefined; // truetsfv.string.orUndefined.testnull; // false
License
tsfv
is available under the ISC license.