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