JavaScript Code Contract Libraries?
I am just starting up a new web application and I want to implement some form of contract'esque style validation in my JavaScript. I did some quick googling, and came across JsContact but the syntax isn't quite what I had in mind. Is anyone aware of other libraries?
I am thinking I want the syntax to be something like
String.prototype.padLeft = function(c, width) {
Verify.value(c).isRequired().isNotNull().isChar();
Verify.value(width).isRequired().isNotNull().isNumber().greaterThan(0);
...
Verify.value(result).isNotNull();
return result;
};
Although it won't take long to put together my own library that has the syntax/methods I want, if someone else has already done the work and it is close enough, it will save me some time. Thanks in advance.
UPDATE
I won't have time to work on this until this afternoon, so I will give it a few more hours to see if anyone has any recommendations. If not, I will post whatever I create up somewhere as an answer below for other people to reference if they desire.
I have also given a little more thought to the API that would make sense, and I am currently thinking something like (contrived examples):
function searchUser(firstName, middleInit, lastName) {
Verify.value(firstName).isString().matching(/\w+/); // Must have value
Verify.value(middleInit).whenNotNull().isChar(); // May be null, but not undefined
Verify.value(lastName).isString().withMinimumLengthOf(2); // Must have value
...
}
function syncTime(serverTime, now) {
Verify.value(serverTime).isDate(); // Must have value.
Verify.value(now).whenDefined().isDate(); // May be undefined, but not null.
}
My current thought is that tolerating NULL or UNDEFINED values is atypical (at least for me?), as such, rather than explicitly specifying that a value .is开发者_运维百科NotNull() you would actually disable the rule for .whenDefined() or .whenNotNull() as shown above. I may make .whenNotNull() not error on UNDEFINED, but I see NULL vs. UNDEFINED as an important distinction; we'll see... all other methods will be pretty typical... thoughts? comments?
Given that no one has recommended any existing libraries, or that I am crazy for thinking this is a good idea I went ahead and threw together a basic library. The code isn't fancy, but it does what I want, and it is reasonably fast to run (approx 40 chained checks per ms in IE).
I settled on a final syntax like:
function syncTime(serverTime, now) {
Verify.value(serverTime).always().isDate(); // Cannot be undefined or null.
Verify.value(now).whenDefined().isDate(); // Cannot be null, but must be date when defined.
//Code
}
function searchForUser(firstName, middleInit, lastName) {
Verify.value(firstName).always().isString().withMinimumLengthOf(2); // Cannot be undefined or null.
Verify.value(lastName).always().isString().withMinimumLengthOf(2); // Cannot be undefined or null.
Verify.value(middleInit).whenNotNull().isChar().between('A', 'Z'); // Cannot be undefined, but must be single char string when not null.
//Code
}
I opted for an explicit 'Must Have Value' via the .always() check, personally I found it nicer to read; but I could see some going another way.
Given that the source is more than I want to post in this answer, please head to this CodePlex Wiki Page if you are interested in the source. I guess it turned in to more of a fluent assertion library; but it does what I need.
Update
I updated the source on the linked CodePlex page above. Specifically, I restructed the Verify class to make use of a 'value context' rather than always building new Verifier objects; improved IE's performance greatly (never was an issue with FireFox or Chrome)... now handles about 100 chained checks per ms in IE.
I may suggest you the next code contracts library: dbc-code-contracts.
NPM: https://www.npmjs.com/package/dbc-code-contracts
GitLab repo (home): https://gitlab.com/o.oleg/orbios.dbc#README
CI-builds with the unit-tests: https://gitlab.com/o.oleg/orbios.dbc/-/jobs/
Sample code:
Dbc.Contract.throwException = true;
const domId = "my-div";
const div = document.createElement("div");
div.id . = domId;
document.body.appendChild(div);
const state = Dbc.Dom.removeById(domId);
Dbc.Contract.isTrue(state);
Dbc.Contract.isNull(document.getElementById(domId));
The following contracts are supported (2nd November, 2017):
- isFunction
- isObject
- isSymbol
- isBoolean
- isTrue
- isFalse
- isString
- isEmptyString
- isNotEmptyString
- areStringsEqual
- isNumber
- isNumberLess
- isNumberBigger
- areNumbersEqual
- isValueNaN
- isDefined
- isUndefined
- isNull
- isArray
- isEmptyArray
- isNotEmptyArray
- isObjectImmutable
- isPromise
- isPrototypeOf
Also, there are internal contract checks, inside the DOM methods from this library.
I also have thrown together my idea of type contracts, which does what I want. A little late, I think, but I'll recommend it nevertheless for people willing to look at it: https://github.com/lindem/FirstContract
It's a WIP, but I needed something like it.
function bmi (weight, height) {
return weight / height * height;
}
var c = require("firstcontract").c
/*
* contract applies to function taking two non-negative numbers,
* returning a negative number:
*/
, contract = c(["R+0", "R+0"], "R+0")
, cbmi = contract(bmi)
;
it was created so that I can add and remove it without changing the function monitored itself and provides just a clamp around the function. It's commonjs and on npm.
One more - https://www.npmjs.com/package/bycontract It's a small library that expects JSDoc expressions (http://usejsdoc.org/) for a contract. A good chance for you are already familiar with the syntax.
Just see it in action:
// Simple test
byContract( true, "boolean" ); // ok
// Multiple Types
byContract( 100, "string|number|boolean" ); // ok
// Optional Parameters
function foo( bar, baz ) {
byContract( arguments, [ "number=", "string=" ] );
}
Here kinda real-world example:
/**
* @param {number|string} sum
* @param {Object.<string, string>} payload
* @param {function} cb
* @returns {HTMLElement}
*/
function foo( sum, payload, cb ) {
// Test if the contract is respected at entry point
byContract( arguments, [ "number|string", "Object.<string, string>", "function" ] );
// ..
var res = document.createElement( "div" );
// Test if the contract is respected at exit point
return byContract( res, HTMLElement );
}
// Test it
foo( 100, { foo: "foo" }, function(){}); // ok
foo( 100, { foo: 100 }, function(){}); // exception - ByContractError: Value of index 1 violates the contract `Object.<string, string>`
精彩评论