Over a million developers have joined DZone.

Guarding Functions in JavaScript

Having the ability to detect and report errors early on in the lifecycle of an error is key to debugging, especially in JavaScript. Read on for some code and how to make this happen easily.

· Web Dev Zone

Start coding today to experience the powerful engine that drives data application’s development, brought to you in partnership with Qlik.

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 lose 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/endpoint 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's exactly what byContract does. Here what 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 afterward.

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( "john.snow@got.com", "email" ); // ok
byContract( "bla-bla", "email" ); // Exception!

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

byContract.is.email = validator.isEmail;

Validation logic belongs 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.

Create data driven applications in Qlik’s free and easy to use coding environment, brought to you in partnership with Qlik.

Topics:
javascript ,npm ,node.js ,code ,functions ,web dev

Published at DZone with permission of Dmitry Sheiko, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}