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.
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.
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
validate([1, 2, 3], "array, !undefined") // true
validate({a: "a"}, "object, !array, !null") // true
validate(5, "!number") // returns false
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: {}
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
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"
validate([5, "abc"], "validArray") // true validate([], "validArray") // false, no empty arrays
validate("test", "validString") // true
validate("", "validString") // false, no empty strings
validate(" ", "validString") // false, whitespace is trimmed
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")
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
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
instructionsproperty 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 thevalidatefunction 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.
-
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
minLengthoneachElement. Workaround: iterate over each element and call validate on it, or add your own validator and use it as a rule foreachElement.
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).