npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

tcomb-validation

v3.4.1

Published

General purpose validation library for JavaScript

Downloads

5,969,530

Readme

build status dependency status npm downloads

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 validate
  • type a type defined with the tcomb library
  • options (optional) is an object with the following keys
    • path: Array<string | number> path prefix for validation
    • context: 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();   // => 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 of ValidationError if validation fails
  • value: an instance of type 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 validate
  • type a type defined with the tcomb library
  • options (optional) is an object with the following keys
    • path: Array<string | number> path prefix for validation
    • context: 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)