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 keys
path: Array<string | number>
path prefix for validationcontext: any
passed to getValidationErrorMessage
(useful for i18n)strict: boolean
(default false
) if true
no additional properties are allowed while validating structs
returns a ValidationResult
object containing the result of the validation
Note.
options
can be an array (as path
prefix) for backward compatibility (deprecated)
Example
var t = require('tcomb-validation');
var validate = t.validate;
validate(1, t.String).isValid();
validate('a', t.String).isValid();
You can inspect the result to quickly identify what's wrong:
var result = validate(1, t.String);
result.isValid();
result.firstError().message;
Primitives
validate('a', t.Nil).isValid();
validate(null, t.Nil).isValid();
validate(undefined, t.Nil).isValid();
validate(1, t.String).isValid();
validate('a', t.String).isValid();
validate('a', t.Number).isValid();
validate(1, t.Number).isValid();
validate(1, t.Boolean).isValid();
validate(true, t.Boolean).isValid();
validate(null, maybe(t.String)).isValid();
validate('a', maybe(t.String)).isValid();
validate(1, maybe(t.String)).isValid();
validate(1, t.Function).isValid();
validate(function () {}, t.Function).isValid();
validate(1, t.Date).isValid();
validate(new Date(), t.Date).isValid();
validate(1, t.RegExp).isValid();
validate(/^a/, t.RegExp).isValid();
Refinements
You can express more fine-grained contraints with the refinement
syntax:
var predicate = function (x) { return x >= 0; };
var Positive = t.refinement(t.Number, predicate);
validate(-1, Positive).isValid();
validate(1, Positive).isValid();
Objects
Structs
var Point = t.struct({
x: t.Number,
y: t.Number
});
validate(null, Point).isValid();
validate({x: 0}, Point).isValid();
validate({x: 0, y: 'a'}, Point).isValid();
validate({x: 0, y: 0}, Point).isValid();
validate({x: 0, y: 0, z: 0}, Point, { strict: true }).isValid();
Interfaces
Differences from structs
- also checks prototype keys
var Serializable = t.interface({
serialize: t.Function
});
validate(new Point(...), Serializable).isValid();
Point.prototype.serialize = function () { ... }
validate(new Point(...), Serializable).isValid();
Lists and tuples
Lists
var Words = t.list(t.String);
validate(null, Words).isValid();
validate(['hello', 1], Words).isValid();
validate(['hello', 'world'], Words).isValid();
Tuples
var Size = t.tuple([Positive, Positive]);
validate([1], Size).isValid();
validate([1, -1], Size).isValid();
validate([1, 2], Size).isValid();
Enums
var CssTextAlign = t.enums.of('left right center justify');
validate('bottom', CssTextAlign).isValid();
validate('left', CssTextAlign).isValid();
Unions
var CssLineHeight = t.union([t.Number, t.String]);
validate(null, CssLineHeight).isValid();
validate(1.4, CssLineHeight).isValid();
validate('1.2em', CssLineHeight).isValid();
Dicts
var Country = t.enums.of(['IT', 'US'], 'Country');
var Warranty = t.dict(Country, t.Number, 'Warranty');
validate(null, Warranty).isValid();
validate({a: 2}, Warranty).isValid();
validate({US: 2, IT: 'a'}, Warranty).isValid();
validate({US: 2, IT: 1}, Warranty).isValid();
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');
MinMax.is('a');
MinMax.is('abcde');
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]
};
validate(mypost, Post).isValid();
validate(mypost, Post).firstError().message;
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;
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
});
var formValues = {
username: $('#username').val().trim() || null,
password: $('#password').val().trim() || null
};
var result = validate(formValues, SignInInfo);
result.isValid();
result.firstError().message;
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();
- 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 of ValidationError
if validation failsvalue
: an instance of type
if validation succeded
var ValidationError = t.struct({
message: t.String,
actual: t.Any,
expected: t.Function,
path: list(t.union([t.String, t.Number]))
}, 'ValidationError');
var ValidationResult = t.struct({
errors: list(ValidationError),
value: t.Any
}, 'ValidationResult');
#isValid()
Returns true if there are no errors.
validate('a', t.String).isValid();
#firstError()
Returns an object that contains an error message or null
if validation succeeded.
validate(1, t.String).firstError().message;
validate(value, type, [options]) -> ValidationResult
value
the value to validatetype
a type defined with the tcomb libraryoptions
(optional) is an object with the following keys
path: Array<string | number>
path prefix for validationcontext: any
passed to getValidationErrorMessage
(useful for i18n)strict: boolean
(default false
) if true
no additional properties are allowed while validating structs
Tests
Run npm test
License
The MIT License (MIT)