Some JavaScript libraries support a feature that I find really interesting. A callback passed to the library can use its formal parameters (or rather their count) to reveal more about its behaviour to the caller.

Let us take the onEnter hook in react-router for instance. This hook for a given route can take one of two possible forms:

Two Parameters:

const myOnEnterHook = ( nextState, replace ) => {
    const { location } = nextState;
    console.log( `Entering ${ location.pathname }...` );
}

Three Parameters:

const myOnEnterHookAsync = ( nextState, replace, next ) => {
    const { location } = nextState;
    console.log( `Entering ${ location.pathname }...` );
    setTimeout(next, 1000);
}

When the hook passed takes three parameters, react-router infers that it is asynchronous in nature and sends a callback as the third argument which must be called for the route transition to complete.

Another place that I saw something similar was in this Intercom library.

It is now clear that the arity of a function can be inspected from code and that is what these libraries are using to support this feature. The question that remains is how. Turns out it is pretty straight-forward. The length property of a function gives you exactly that.

A function’s arity (or length) is the count of the number of parameters that it takes.

Parameters vs Arguments

It is important to know the difference between formal parameters and arguments in the context of a function. The callee has parameters defined in its signature while the caller passes arguments to it. Arguments passed are mapped to parameters based on their position in the list.

A function can take a finite set of formal parameters and still work on variable arguments passed to it using the nifty ES6 rest syntax:

const myOnEnterHook = ( nextState, replace, ...otherArgs ) => {
    // My arity is 2...
    // ...but I get to access all my arguments!
};

Proxy Caveat

A proxy function is generally defined this way:

const proxy = (fn) => {
    return (...args) => {
        /* Do something here. */
        return fn.apply(fn, args);
    };
};

As the returned function is variadic though, its length is set to 0 in Javascript. So, by proxying, we lose the arity of the original function. This is a problem as the arity of the original function is of value to the caller as we have seen in the examples above.

We need a way by which we can reset the length of the proxy function to the arity of the original. The util-arity module lets us do just that.

Here is the rewritten proxy method using util-arity:

import setArity from 'util-arity';

const proxyWithArity = (fn) => {
    let proxy = (...args) => {
        /* Do something here */
        return fn.apply(fn, args);
    };
    return setArity(fn.length, proxy);
};