Skip to content

borisjoffe/validator.js

Repository files navigation

Validator.js

Motivation

I wanted to build a lightweight extensible module capable of strict and composable validation for JSON responses, internal data models, and function arguments. It can also be used for form validation.

Setup

Adding the validator.js file will add the validate() function to the global namespace. To add the function onto a custom object, change the this keyword on the last line of the file to the name of your custom object.

Basic/composite validation

validate(5, "number, !null")           // true, same as typeof val === "number" && val !== null
validate([1, 2, 3], "array")           // true, same as Array.isArray(val)
validate({a: "a"}, "object, !array")   // true, same as typeof val === "object" && !Array.isArray(val)
validate({a: "a"}, "strictObject")     // true, equivalent to "object, !array, !null"
validate([3, "a"], "strictObject")     // false
validate(null, "strictObject")         // false

Negation of validation

validate([1, 2, 3], "array, !undefined")      // true
validate({a: "a"}, "object, !array, !null")   // true
validate(5, "!number")                        // returns false

Throwing errors on validation failures

If the 3rd argument, throwErrorMessage, is a string, it will throw an error on failed validation. You should use the string to describe the value you’re validating to get clear error messages.

validate([5], "number", "number of requests") will throw the following error:

TypeError: Validation failed on rule "number".
number of requests was: [5]
options were: {}

Property validation

Validate properties like length, minLength, maxLength, max, and min. Prepend "strict" to any max/min validator to get strictly less than or greater than functionality.

validate("hello", "string", {minLength: 2})      // true
validate("5", "string", {max: 12})               // true
validate(16, "number", {min: 12})                // true
validate(12, "number", {strictMin: 12});         // false
validate("a", "string", {maxLength: 2});         // false
validate("hello world", "string", {length: 2});	 // false

Useful composite aliases

Objects

Goal: avoid errors from Arrays and nulls being typeof Objects

validate({a: "a"}, "validObject")  // true
validate({}, "validObject")        // false, we don't wan't empty objects
validate(null, "validObject")      // false, avoid typeof null === "object"
validate([a, 2], "validObject")    // false, avoid typeof [] === "object"
Arrays
validate([5, "abc"], "validArray")  // true
validate([], "validArray")          // false, no empty arrays
Strings
validate("test", "validString")   // true
validate("", "validString")       // false, no empty strings
validate("  ", "validString")     // false, whitespace is trimmed
Numbers

Goal: avoid +/-Infinity, NaN; avoid nulls, Booleans, and Arrays being coerced to a Number

Use validNumeric/validIntegerish if numbers as strings are ok

Otherwise, use validNumber and validInteger

validate(500, "validNumber")                     // true
validate(10, "validNumber", {min: 2, max: 100})  // true
validate(0xFF, "validNumber")                    // true, hexadecimals and octal are ok
validate(90e2, "validNumber")                    // true, scientific notation is ok
validate(Infinity, "validNumber")                // false, Infinity and -Infinity are not valid
validate(NaN, "validNumber")                     // false, avoid typeof NaN === "number"

// Numeric validates same as Number but allows string values
validate("90e2", "validNumeric")    // true
validate("0xEC", "validNumeric")    // true
validate(null, "validNumeric")      // false, avoid isFinite(null) === true
validate(true, "validNumeric")      // false, avoid isFinite(true) === true
validate([1], "validNumeric")       // false, avoid isFinite([]) === true

validate(92, "validInteger")        // true
validate(NaN, "validInteger")       // false
validate(5.2, "validInteger")       // false
validate(Infinity, "validInteger")  // false

Note: validate(x, "validNumber") is a composite alias for validate(x, "!null, !boolean, !array, finite, number")

Similarly, validate(x, "validNumeric") is a composite alias for validate(x, "!null, !boolean, !array, finite")

JSON validation

Iterate through array or object and validate either each element or a property of each element

validate({a: 9, b: 3}, "eachElement", {rules: "validNumber"})     // true
validate({a: NaN, b: 3}, "eachElement", {rules: "validNumber"})   // false
validate([{b: 9}, {b: 2.2}],  "eachElementProperty", {propertyName: "b", rules: "validNumber"}); // true
validate([{b: 9}, {b: 2.2}],  "eachElementProperty", {propertyName: "b", rules: "validInteger"}); // false, 2.2 is not an integer

Design/Extensibility

Base validators are functions defined on the validate.validators object. To add a custom validator, use:

validate.validators.containsHello = function (x) {
	return x.contains("hello");
};

validate.validators.containsWorld = function (x) {
	return x.contains("world");
};

Composite validators are arrays or comma delimited strings defined on validate.compositeValidators. To add a custom one, use:

validate.compositeValidators.containsHelloWorld = ["containsHello", "containsWorld"];

Aliases are single element arrays of a string or strings with no commas defined on validate.aliases e.g. validate.aliases.isHelloWorld = "containsHelloWorld";

  • Data Validation Example (page 156) in Javascript Patterns by Stoyan Stefanov - has one layer deep object validation and a similar overall design but no composite validators.

    • It is more geared for form validation since each validator has an instructions property that can be displayed to the user.

    • One could define a property on a base validator function or a composite validator array or string e.g. validate.validators.containsHelloWorld.instructions = "Must contain hello world" and then slightly change the validate function to achieve the same functionality.

  • CERNY.js - specifically Schema, Contracts, and Type checking. Has more advanced capabilities on some fronts but is also part of a larger library, has less base validators, and does not support composite validators.

  • is.js - has fewer validators, no composite validators, and no deep validation

  • upcast.js - has type casting support but has fewer validators, no composite validators, and no deep validation.

Shortcomings

  • Can only go 2 levels deep into objects/array. Workaround: iterate over the object/array yourself and call validate on the properties.

  • Cannot combine property validation with JSON validation e.g. cannot validate minLength on eachElement. Workaround: iterate over each element and call validate on it, or add your own validator and use it as a rule for eachElement.

Tests

Currently, there are ~17 Jasmine unit tests written. Run grunt test to execute them.

Most base and composite validators will have at least one test for them in the future. More extensive tests will be written for throwing errors, property validation (like {min: 2}), and JSON validation (like eachElement, eachElementProperty).

Dependencies

  • ECMAScript 5 (Node and browser environments supported)

  • Jasmine (for tests)

License

About

Validate Javascript data and types

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published