Guarding Functions in JavaScript

JavaScript

As developers we spend a lot of our time on debugging and particularly on spotting the source of a problem. DevTools guides us though the call stack, but the tracing process can be still pretty time consuming, especially on a cascade of asynchronous calls. The remedy here is early problem reporting.

Let’s say we have a function to search trough a multidimensional structure for the elements containing a given string. We make a call that looks like legit:

grep( "substring", tree );

Yet we don’t get the expected result. We would spend some time on examining the given tree structure and it can be quite a big one. Then we would probably do other checks, but eventually we would find out from the code of the function that it expects the arguments in the opposite order. Thus if we had just guarded the function parameters, we would not lost all of this time:

function grep( tree, substring ){
  if ( !( tree instanceof Tree ) ) {
    throw TypeError( "Invalid tree parameter" );
  }
  if ( typeof substring !== "string" ) {
    throw TypeError( "Invalid substring parameter" );
  }
  //...
}

This kind of validation is a part of Design by Contract approach . It states for validation of preconditions and postconditions within a software component. In our case we have to test our function input against a specified contract (tree is an instance of Tree and substring is a string) and advisable we check the output to be a string.

Yeah, JavaScript doesn’t have currently built-in facilities for entry/end point validation like other languages. For an instance PHP has type hinting:

<?php
function grep( Tree $tree, string $substring ): string {}

TypeScript has strict types:

function grep( tree: Tree, substring: string ): string {}

in addition it supports also advanced types (union, optional, intersection, generics and others):

function normalize( numberLike: number | string, modifier?: boolean ): string {}

Among the features proposed for ES.Next there is one called Guards that suggests the following syntax:

function grep( tree:: Tree, substring:: String ):: String {}

Nowadays in JavaScript we have to cope with external libraries or transpilers. However just a few can be found. One of the oldest libraries is Cerny.js . It is very much of DbC, powerful and flexible:

var NewMath = {};
(function() {
    var check = CERNY.check;
    var pre = CERNY.pre;
    var method = CERNY.method;

    // The new division
    function divide(a,b) {
      return a / b;
    }
    method(NewMath, "divide", divide);
    // The precondition for a division
    pre(divide, function(a,b) {
       check(b !== 0, "b may not be 0");
    });
})();

But as for me, it’s too complex to read. I would prefer something concise and clean just to test pre-/postcoditions. The syntax provided by Contractual is very much of what I mean:

function divide ( a, b ) {
  pre:
    typeof a === "number";
    typeof b === "number";
    b !== 0, "May not divide by zero";
  main:
    return a / b;
  post:
    __result < a;
}
alert(divide(10, 0));

Everything looks fine except it’s no JavaScript. One has to compile the sources to JavaScript with Contractual or Babel Contracts. I have nothing against transpilers, but if to use one, I would rather go with TypeScript.

But coming back to JavaScript, have you every realized that regardless of libraries and frameworks we keep already declaring entry/exit point contracts when commenting functions and classes with JSDoc. It would be just perfect if doc comments were used for validation. As you understand, it cannot be done without compilation. However we can use a library that relies on JSDoc expressions. Fortunately that exactly what byContract does. Here how the syntax look like:

/**
 * @param {number|string} sum
 * @param {Object.<string, string>} dictionary
 * @param {function} transformer
 * @returns {HTMLElement}
 */
function makeTotalElement( sum, dictionary, transformer ) {
  // 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
var el1 = makeTotalElement( 100, { foo: "foo" }, function(){}); // ok
var el2 = makeTotalElement( 100, { foo: 100 }, function(){}); // exception

As you see we can copy/paste types from the doc comment to byContract and that makes a contract, that simple. Let’s examine it more closely. byContract can be accessed as a UMD module (both AMD/CommonJS-compliant) or as a global variable. We can pass to it either value/JSDoc expression pair

byContract( value, "JSDOC-EXPRESSION" );

or list of values against a list of expressions:

byContract( [ value, value ], [ "JSDOC-EXPRESSION", "JSDOC-EXPRESSION" ] );

byContract tests the values and if the associated contract (as JSDoc expression) violated it throws byContract.Exception (which is an instance of TypeError) with a message like Value violates the contract NaN.

In the simplest case the contract is set to validate against a primitive type like array, string, undefined, boolean, function, nan, null, number, object, regexp:

byContract( true, "boolean" );

When we need to allow value to be one of a list of specified types we can use type union

byContract( 100, "string|number|boolean" );

A function can have mandatory as well as optional parameters. By default a parameter provided with a primitive type in the contract is considered mandatory. But with ‘=’ modifier we can set it as optional. So byContract that treats e.g. number= like number|undefined

function foo( bar, baz ) {
  byContract( arguments, [ "number=", "string=" ] );
}

Following JSDoc nullable/non-nullable types also supported

byContract( 42, "?number" ); // a number or null.
byContract( 42, "!number" ); // a number, but never null.

Of course we can use interfaces for a contract. Thus we can refer any available in the scope objects, including JavaScript built-in interfaces:

var instance = new Date();
byContract( instance, "Date" );
byContract( view, "Backbone.NativeView" );
byContract( e, "Event" );

For arrays and objects we can optionally validate the content. So we can state that e.g. all of array values must be numbers or all the keys and values of an object are strings:

byContract( [ 1, 1 ], "Array.<number>" );
byContract( { foo: "foo", bar: "bar" }, "Object.<string, string>" );

It may serve well for linear structures, but useless otherwise. So alternatively we can create a type definition describing the content of an object (see byContract.typedef) and refer it as a type afterwards

byContract.typedef( "Hero", {
  hasSuperhumanStrength: "boolean",
  hasWaterbreathing: "boolean"
});
var superman = {
  hasSuperhumanStrength: true,
  hasWaterbreathing: false
};
byContract( superman, "Hero" );

This example defines a type Hero that represents an object/namespace required to have properties hasSuperhumanStrength and hasWaterbreathing both of boolean type.

All the described methods validate values by types, but what about invariants? We can wrap the constraint in a custom type. Let’s say for testing string is an email address we can add a validator like that:

byContract.is.email = function( val ){
  var re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
  return re.test( val );
}
byContract( "[email protected]", "email" ); // ok
byContract( "bla-bla", "email" ); // Exception!

Actually you probably don’t need event to write a validaton function, but use an external library (e.g. validator) instead:

byContract.is.email = validator.isEmail;

Validation logic belong to the development environment. With byContract we can disable the validation globally with a trigger:

if ( env !== "dev" ) {
  byContract.isEnabled = false;
}

byContract is a small validation library (~1KB gzip) that allows you to benefit from Design by Contract programming in your JavaScript code.