tcomb-validation v3.4.1
A general purpose JavaScript validation library based on type combinators
Features
- concise yet expressive syntax
- validates native types, refinements, objects, lists and tuples, enums, unions, dicts, intersections
- validates structures with arbitrary level of nesting
- detailed informations on failed validations
- lightweight alternative to JSON Schema
- reuse your domain model written with tcomb
Documentation
Basic usage
If you don't know how to define types with tcomb you may want to take a look at its README file.
The main function is validate
:
validate(value, type, [options]) -> ValidationResult
value
the value to validatetype
a type defined with the tcomb libraryoptions
(optional) is an object with the following keyspath: Array<string | number>
path prefix for validationcontext: any
passed togetValidationErrorMessage
(useful for i18n)strict: boolean
(defaultfalse
) iftrue
no additional properties are allowed while validating structs
returns a ValidationResult
object containing the result of the validation
Note.
options
can be an array (aspath
prefix) for backward compatibility (deprecated)
Example
var t = require('tcomb-validation');
var validate = t.validate;
validate(1, t.String).isValid(); // => false
validate('a', t.String).isValid(); // => true
You can inspect the result to quickly identify what's wrong:
var result = validate(1, t.String);
result.isValid(); // => false
result.firstError().message; // => 'Invalid value 1 supplied to String'
// see `result.errors` to inspect all errors
Primitives
// null and undefined
validate('a', t.Nil).isValid(); // => false
validate(null, t.Nil).isValid(); // => true
validate(undefined, t.Nil).isValid(); // => true
// strings
validate(1, t.String).isValid(); // => false
validate('a', t.String).isValid(); // => true
// numbers
validate('a', t.Number).isValid(); // => false
validate(1, t.Number).isValid(); // => true
// booleans
validate(1, t.Boolean).isValid(); // => false
validate(true, t.Boolean).isValid(); // => true
// optional values
validate(null, maybe(t.String)).isValid(); // => true
validate('a', maybe(t.String)).isValid(); // => true
validate(1, maybe(t.String)).isValid(); // => false
// functions
validate(1, t.Function).isValid(); // => false
validate(function () {}, t.Function).isValid(); // => true
// dates
validate(1, t.Date).isValid(); // => false
validate(new Date(), t.Date).isValid(); // => true
// regexps
validate(1, t.RegExp).isValid(); // => false
validate(/^a/, t.RegExp).isValid(); // => true
Refinements
You can express more fine-grained contraints with the refinement
syntax:
// a predicate is a function with signature: (x) -> boolean
var predicate = function (x) { return x >= 0; };
// a positive number
var Positive = t.refinement(t.Number, predicate);
validate(-1, Positive).isValid(); // => false
validate(1, Positive).isValid(); // => true
Objects
Structs
// an object with two numerical properties
var Point = t.struct({
x: t.Number,
y: t.Number
});
validate(null, Point).isValid(); // => false
validate({x: 0}, Point).isValid(); // => false, y is missing
validate({x: 0, y: 'a'}, Point).isValid(); // => false, y is not a number
validate({x: 0, y: 0}, Point).isValid(); // => true
validate({x: 0, y: 0, z: 0}, Point, { strict: true }).isValid(); // => false, no additional properties are allowed
Interfaces
Differences from structs
- also checks prototype keys
var Serializable = t.interface({
serialize: t.Function
});
validate(new Point(...), Serializable).isValid(); // => false
Point.prototype.serialize = function () { ... }
validate(new Point(...), Serializable).isValid(); // => true
Lists and tuples
Lists
// a list of strings
var Words = t.list(t.String);
validate(null, Words).isValid(); // => false
validate(['hello', 1], Words).isValid(); // => false, [1] is not a string
validate(['hello', 'world'], Words).isValid(); // => true
Tuples
// a tuple (width x height)
var Size = t.tuple([Positive, Positive]);
validate([1], Size).isValid(); // => false, height missing
validate([1, -1], Size).isValid(); // => false, bad height
validate([1, 2], Size).isValid(); // => true
Enums
var CssTextAlign = t.enums.of('left right center justify');
validate('bottom', CssTextAlign).isValid(); // => false
validate('left', CssTextAlign).isValid(); // => true
Unions
var CssLineHeight = t.union([t.Number, t.String]);
validate(null, CssLineHeight).isValid(); // => false
validate(1.4, CssLineHeight).isValid(); // => true
validate('1.2em', CssLineHeight).isValid(); // => true
Dicts
// a dictionary of numbers
var Country = t.enums.of(['IT', 'US'], 'Country');
var Warranty = t.dict(Country, t.Number, 'Warranty');
validate(null, Warranty).isValid(); // => false
validate({a: 2}, Warranty).isValid(); // => false, ['a'] is not a Country
validate({US: 2, IT: 'a'}, Warranty).isValid(); // => false, ['IT'] is not a number
validate({US: 2, IT: 1}, Warranty).isValid(); // => true
Intersections
var Min = t.refinement(t.String, function (s) { return s.length > 2; }, 'Min');
var Max = t.refinement(t.String, function (s) { return s.length < 5; }, 'Max');
var MinMax = t.intersection([Min, Max], 'MinMax');
MinMax.is('abc'); // => true
MinMax.is('a'); // => false
MinMax.is('abcde'); // => false
Nested structures
You can validate structures with an arbitrary level of nesting:
var Post = t.struct({
title: t.String,
content: t.String,
tags: Words
});
var mypost = {
title: 'Awesome!',
content: 'You can validate structures with arbitrary level of nesting',
tags: ['validation', 1] // <-- ouch!
};
validate(mypost, Post).isValid(); // => false
validate(mypost, Post).firstError().message; // => 'tags[1] is `1`, should be a `Str`'
Customise error messages
You can customise the validation error message defining a function getValidationErrorMessage(value, path, context)
on the type constructor:
var ShortString = t.refinement(t.String, function (s) {
return s.length < 3;
});
ShortString.getValidationErrorMessage = function (value) {
if (!value) {
return 'Required';
}
if (value.length >= 3) {
return 'Too long my friend';
}
};
validate('abc', ShortString).firstError().message; // => 'Too long my friend'
How to keep DRY?
In order to keep the validation logic in one place, one may define a custom combinator:
function mysubtype(type, getValidationErrorMessage, name) {
var Subtype = t.refinement(type, function (x) {
return !t.String.is(getValidationErrorMessage(x));
}, name);
Subtype.getValidationErrorMessage = getValidationErrorMessage;
return Subtype;
}
var ShortString = mysubtype(t.String, function (s) {
if (!s) {
return 'Required';
}
if (s.length >= 3) {
return 'Too long my friend';
}
});
Use cases
Form validation
Let's design the process for a simple sign in form:
var SignInInfo = t.struct({
username: t.String,
password: t.String
});
// retrieves values from the UI
var formValues = {
username: $('#username').val().trim() || null,
password: $('#password').val().trim() || null
};
// if formValues = {username: null, password: 'password'}
var result = validate(formValues, SignInInfo);
result.isValid(); // => false
result.firstError().message; // => 'Invalid value null supplied to /username: String'
JSON schema
If you don't want to use a JSON Schema validator or it's not applicable, you can just use this lightweight library in a snap. This is the JSON Schema example of http://jsonschemalint.com/
{
"type": "object",
"properties": {
"foo": {
"type": "number"
},
"bar": {
"type": "string",
"enum": [
"a",
"b",
"c"
]
}
}
}
and the equivalent tcomb-validation
counterpart:
var Schema = t.struct({
foo: t.Number,
bar: t.enums.of('a b c')
});
let's validate the example JSON:
var json = {
"foo": "this is a string, not a number",
"bar": "this is a string that isn't allowed"
};
validate(json, Schema).isValid(); // => false
// the returned errors are:
- Invalid value "this is a string, not a number" supplied to /foo: Number
- Invalid value "this is a string that isn't allowed" supplied to /bar: "a" | "b" | "c"
Note: A feature missing in standard JSON Schema is the powerful refinement syntax.
Api reference
ValidationResult
ValidationResult
represents the result of a validation. It containes the following fields:
errors
: a list ofValidationError
if validation failsvalue
: an instance oftype
if validation succeded
// the definition of `ValidationError`
var ValidationError = t.struct({
message: t.String, // a default message for developers
actual: t.Any, // the actual value being validated
expected: t.Function, // the type expected
path: list(t.union([t.String, t.Number])) // the path of the value
}, 'ValidationError');
// the definition of `ValidationResult`
var ValidationResult = t.struct({
errors: list(ValidationError),
value: t.Any
}, 'ValidationResult');
#isValid()
Returns true if there are no errors.
validate('a', t.String).isValid(); // => true
#firstError()
Returns an object that contains an error message or null
if validation succeeded.
validate(1, t.String).firstError().message; // => 'value is `1`, should be a `Str`'
validate(value, type, options) -> ValidationResult
value
the value to validatetype
a type defined with the tcomb libraryoptions
(optional) is an object with the following keyspath: Array<string | number>
path prefix for validationcontext: any
passed togetValidationErrorMessage
(useful for i18n)strict: boolean
(defaultfalse
) iftrue
no additional properties are allowed while validating structs
Tests
Run npm test
License
The MIT License (MIT)
7 years ago
7 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago