Home > Software design >  Not all constituents of type 'number | function' are callable
Not all constituents of type 'number | function' are callable

Time:08-19

Consider a simple function like this:

export const add = (num?: number) => {
    const adder = (n: number) => {
        if (!n) {
            return num;
        }
        num = (num && num   n) || n;
        return adder;
    };

    return adder;
};

Example usage:

const result = add(1)(2)(3)() // => 6

When called, add will either return next function that takes another number, or a final sum if no number is passed.

This would work as expected in plain js, however for typescript this will cause an error:

This expression is not callable. Not all constituents of type 'number | ((n?: number | undefined) => (x?: number | undefined) => number | ...)' are callable. Type 'number' has no call signatures.ts(2349)

This is because TS cannot determine if the next iteration returns a function or a number.

Question:

How to type this function correctly, so that TS does not throw an Error?

CodePudding user response:

It is certainly possible to describe the call signature of add() so that callers will see the desired behavior. Unfortunately the TypeScript compiler will not be able to verify that the implementation of add() satisfies this call signature; it would need to be smarter about overload implementations or the behavior of generic conditional types to do so. This means we'll need at least one type assertion or similar type-loosening technique to suppress compiler errors.

Anyway, let's say that add is a function of a type I'll call Adder:

interface Adder {
  (n: number): Adder;
  (n?: undefined): number
}

An Adder is an overloaded function with two call signatures; either you call it with a number in which case another Adder is returned, or you call it with no arguments (or I guess an undefined one) in which case you get a number. Now we can assert that add is an Adder:

const add = ((num?: number) => {
    const adder = (n: number) => {
        if (!n) {
            return num;
        }
        num = (num && num   n) || n;
        return adder;
    };

    return adder;
}) as Adder; // <-- assertion

And then things behave as expected:

const result = add(1)(2)(3)() // no error now

console.log(result); // 6

Playground link to code

  • Related