0

Edit: I found this interesting library which looks like it can do exactly what I was describing at the bottom: https://github.com/philbooth/check-types.js

Looks like you can do it by calling check.quacksLike.


I'm fairly new to using javascript and I'm loving the amount of power it offers, but sometimes it is too flexible for my sanity to handle. I would like an easy way to enforce that some argument honors a specific interface.

Here's a simple example method that highlights my problem:

var execute = function(args)
{
    executor.execute(args);
}

Let's say that the executor expects args to have a property called cmd. If it is not defined, an error might be caught at another level when the program tries to reference cmd but it is undefined. Such an error would be more annoying to debug than explicitly enforcing cmd's existence in this method. The executor might even expect that args has a function called getExecutionContext() which gets passed around a bit. I could imagine much more complex scenarios where debugging would quickly become a nightmare of tracing through function calls to see where an argument was first passed in.

Neither do I want to do something on the lines of:

var execute = function(args)
{
    if(args.cmd === undefined || args.getExecutionContext === undefined ||
       typeof args.getExecutionContext !== 'function')
        throw new Error("args not setup correctly");
    executor.execute(args);
}

This would entail a significant amount of maintenance for every function that has arguments, especially for complex arguments. I would much rather be able to specify an interface and somehow enforce a contract that tells javascript that I expect input matching this interface.

Maybe something like:

var baseCommand =
{
    cmd: '',
    getExecutionContext: function(){}
};

var execute = function(args)
{
    enforce(args, baseCommand); //throws an error if args does not honor
                                //baseCommand's properties
    executor.execute(args);
}

I could then reuse these interfaces amongst my different functions and define objects that extend them to be passed into my functions without worrying about misspelling property names or passing in the wrong argument. Any ideas on how to implement this, or where I could utilize an existing implementation?

Alex
  • 759
  • 5
  • 12

3 Answers3

0

I don't see any other way to enforce this. It's one of the side effects of the dynamic nature of JavaScript. It's essentially a free-for-all, and with that freedom comes responsibility :-)

TGH
  • 38,769
  • 12
  • 102
  • 135
  • It's not really a matter of responsibility though, it's more like preventative care. I am all for devising and following standards to keep structured code, but code is always error prone. Inevitably you are going to make mistakes, and when you do I would like it to be easier to debug than not. – Alex Jul 19 '13 at 01:29
  • @Alex I agree with your statement that JavaScript can be unsafe. People within your team might call constructor functions, pass wrong type arguments, set or delete private variables and probably some other dangers. You can't take away JavaScript's flexibility to gain these checks but you can use tools to compile your code for type checking. – HMR Jul 19 '13 at 01:38
0

If you're in need of type checking you could have a look at typescript (it's not JavaScript) or google's closure compiler (javascript with comments).

Closure compiler uses comments to figure out what type is expected when you compile it. Looks like a lot of trouble but can be helpful in big projects.

There are other benefits that come with closure compiler as you will be forced to produce comments that are used in an IDE like netbeans, it minifies your code, removes unused code and flattens namespaces. So code organized in namespaces like myApp.myModule.myObject.myFunction will be flattened to minimize object look up.

Cons are that you need to use externs when you use libraries that are not compiler compatible like jQuery.

HMR
  • 37,593
  • 24
  • 91
  • 160
  • Does typescript limit functionality from javascript? Or does it just allow error checking? I would still like to be able to do things like dynamically adding properties to an object. And how does integrating it with javascript libraries work? – Alex Jul 19 '13 at 01:35
  • @Alex Typescript is JavaScript with added features. If you don't use the features it is just JavaScript and you don't need to compile it. Closure compiler uses the comments in JavaScript to do type checking but if you have `I know what I'm doing` code you can switch off these checks per file or function with the surpress tag. – HMR Jul 19 '13 at 01:52
0

The way that this kind of thing is typically dealt with in javascript is to use defaults. Most of the time you simply want to provide a guarentee that certain members exist to prevent things like reference errors, but I think that you could use the principal to get what you want.

By using something like jQuery's extend method, we can guarentee that a parameter implements a set of defined defaults.

    var defaults = {
        prop1: 'exists',
        prop2: function() { return 'foo'; }
    };

    function someCall(args) {
        var options = $.extend({}, defaults, args);
        // Do work with options... It is now guarentee'd to have members prop1 and prop2, defined by the caller if they exist, using defaults if not.
    }

If you really want to throw errors at run time if a specific member wasn't provided, you could perhaps define a function that throws an error, and include it in your defaults. Thus, if a member was provided by the caller, it would overwrite the default, but if it was missed, it could either take on some default functionality or throw an error as you wish.

Sethcran
  • 798
  • 5
  • 11
  • I actually use default args in a lot of places. But in this case I don't want to specify default behavior, I want to ensure that the passed arguments specify that behavior. If I give it default behavior, more subtle errors could occur by accidentally passing in an incomplete interface. Throwing exceptions in the default args are better than leaving it undefined but that still throws the error at a different level, and that won't work for non function properties. – Alex Jul 19 '13 at 14:02