Handling the unexpected - Type safe functions in Javascript

This is heavy stuff. I do not expect you to understand it, if you are a beginner in Javascript. I try to explain everything in detail, so maybe you should give it a try. I've written a jQuery plugin that makes type safe functions, so if you don't understand how it's done, you can use it nonetheless...
So what do we want? We want some way to automatedly check the given arguments by just telling which types we expect. We want to reject all other values. We want to reject the incorrect number of arguments. Let's start step by step.
return f;
};
We define a makeTypeSafe-function that accepts two parameters: f, the function to make type safe, and parameterList, the list of expected types of arguments of f. This function does nothing so far except returning the original function.
Now reject the wrong number of arguments:
// return a function that first checks the arguments before calling the
// original function
return function () {
// check number of arguments
if (arguments.length !== parameterList.length) {
throw "Unexpected number of arguments. Expected " + p + ", got " + arguments.length;
}
// call f, passing the arguments, preserving the context
return f.apply(this, arguments);
};
};
Here we do no longer return the original function but a new function. This new function checks if the number of arguments (arguments.length) is different from the expected number of arguments (parameterList.length). If so, it throws an error. If not, f is called (and its return value returned).
Need an example of how to use it? Here you are.
return a + b;
};
var addIntegers = makeTypeSafe(add, ["int", "int"]);
addIntegers(21); // will throw an error
addIntegers(21,15); // will return 36
addIntegers("abc", 42); // will not throw an error
Why doesn't the last call throw an error? Because until now we only check the number of arguments, not the type. Let's change that.
"int" : function (n) {
// by comparing n to its floor value we see if it's an integer
return n === Math.floor(n);
}
};
Here we define an object with one property, a function called int, which checks if a given argument is an integer. Since there is no integer type in Javascript, we do this by comparing n to its floor value (which is the same for integers).
Extending the makeTypeSafe function to check the types of arguments leads to the following code:
"int" : function (n) {
return n === Math.floor(n);
}
};
var makeTypeSafe = function (f, parameterList) {
return function () {
// check number of arguments
if (arguments.length !== parameterList.length) {
throw "Unexpected number of arguments. Expected " + p + ", got " + arguments.length + ".";
}
// check every argument using the types functions defined above
for (var i = 0, l = arguments.length; i < l; i++) {
if (!types[parameterList[i]](arguments[i])) {
throw "Invalid argument at " + i + ". Argument must be of type " + parameterList[i] + ".";
}
}
// call original function
return f();
};
};
That's basically the way to automatedly check parameters before executing the original function. Now we can extend the types object to accept more types:
"int" : function (n) {
// by comparing n to its floor value we see if it's an integer
return n === Math.floor(n);
},
"double" : function (n) {
// NaN is a number as well, so check that n is not NaN
return typeof n === "number" && !isNaN(n);
},
"string" : function (n) {
return typeof n === "string";
}
};
We could even add more sophisticated types like natural numbers or arrays of integers.
// replace > by >= if 0 is natural to you
return types["int"](n) && n > 0;
};
types["int[]"] : function (n) {
// accept only arrays
if (!(n instanceof Array)) {
return false;
}
// check if every element is an integer
for (var i = 0, l = n.length; i < l; i++) {
if (!types["int"](n[i])) {
return false;
}
}
return true;
};
"Make a jQuery plugin!" my fellow co-author Christian cried. Well, personally I don't fancy jQuery, but if you must make a function type safe, you might be using jQuery anyway, so why not? I extended the list of build-in types to support int, double (alias float), string, boolean, char, object and arrays of those types as well (used via int[], double[] etc). Furthermore, the list of of expected parameters is checked for unknown types.
I solved the original function to calculate the greatest common divisor
var gcd = function (a, b) {
if (a === b) {
return a;
}
if (a === 1 || b === 1) {
return 1;
}
if (a < b) {
return gcd(a, b - a);
}
return gcd(a - b, b);
};
// extend the known types to support natural numbers
jQuery.makeTypeSafe.types["natural"] = function (n) {
return jQuery.makeTypeSafe.types["int"](n) && n > 0;
};
// make gcd type safe
var greatestCommonDivisor = jQuery.makeTypeSafe(gcd, ["natural", "natural"]);
// call type safe gcd function
greatestCommonDivisor(21, 15); // returns 3
greatestCommonDivisor("21", "15"); // throws an error
Any questions, any remarks? Feel free to comment!
Trackback URL for this post:
| Attachment | Size |
|---|---|
| jquery.makeTypeSafe.js.txt | 4.55 KB |

Comments
Anonymous - Wed, 08/05/2009 - 14:31
On a sidenote and you do not want to use the typesafe function wrapper, instead of doing this:
You can do like this:
A little cleaner IMHO.
Matthias Reuter - Thu, 08/06/2009 - 11:17
I wanted to keep my code easy to understand, so I explicitely split this in two parts.
In a draft version I had this:
return n + m;
}
which is correct but somewhat obscure.
Anonymous - Thu, 08/06/2009 - 21:28
That is sweet. I'm a simple douche but would have preferred that you had this code in your demo,
Vincent (not verified) - Mon, 05/03/2010 - 09:12
You have a typo in your makeTypeSafe function: this line
contains 'p', which should be parameterList.length.
Btw, I have done similar stuff, yet not completely generic so far. However, I preferred using object references instead of strings for types, like:
This makes the type checking functions superfluous (note, however, that basic types like int are somewhat sloppy in javascript, so you will require explicit casting to an object variant for these).