Reputation: 25
I wrote a short example to explain the situation occurred to my code.
I have two functions resembles to following:
function sum3(a: number, b: number, c: number): number {
return a + b + c
}
function plus1(...args: number[]): number[] {
return args.map(x => x + 1)
}
function sum3
, the first one, needs exact 3 number arguments. e.g. sum3(1, 2, 3)
returns 6
.
function plus
, the second one, has no limit on the number of arguments. e.g. plus1(100, 200)
returns Array of number [101, 201]
, plus1(300, 400, 500)
returns [301, 401, 501]
.
The error happened when I tried to call the function sum3
with spread syntax.
// javascript returns 303
// typescript throws an error: Expected 3 arguments, but got 1 or more. (TS2556)
sum3(101, ...plus1(100, 100))
My typescript version is the newest, 4.2.4. Is this a bug?
Upvotes: 0
Views: 1289
Reputation: 10127
Well I actually solved your problem, but I got another error which I cannot solve right now.
This implementation returns a type with the correct number of elements in the tuple, without a need for overloads:
function sum3(a: number, b: number, c: number): number {
return a + b + c;
}
function plus1<T extends Array<number>>(...args: T): [...T] {
return args.map(x => x + 1); // Target requires 1 element(s) but source may have fewer.
// return args.map(x => x + 1) as [...T]; // No error with casting
}
sum3(101, ...plus1(100, 100)); // [100, 100]
sum3(101, ...plus1(100, 100, 100)); // [100, 100, 100], Expected 3 arguments, but got 4.
I believe the reason for the error is that Array.map typings don't use variadic tuples, they return arrays instead.
Upvotes: 1
Reputation: 1074148
No, it's not a bug. Think about the general case: plus1
just says it returns number[]
. That could have zero, one, two, three, four, five, or any other number of elements in it. But sum3
expects exactly three (one from the other argument, and two from plus1
).
One solution is a bunch of explicit overloads (playground link):
function plus1(a: number, b: number): [number, number];
function plus1(a: number, b: number, c: number): [number, number, number];
function plus1(a: number, b: number, c: number, d: number): [number, number, number, number];
function plus1(a: number, b: number, c: number, d: number, e: number): [number, number, number, number, number];
function plus1(...args: number[]): number[] {
return args.map(x => x + 1)
}
Then your call works, because it matches the function plus1(a: number, b: number): [number, number]
signature. That would work for up to five arguments, after which the overload returning number[]
would kick in.
Another solution is to use a type assertion on your plus1
call saying that it returns [number, number]
, not number[]
(playground link):
sum3(101, ...(plus1(100, 100) as [number, number]));
...but type assertions are best avoided. For example, if you added a third argument to your plus1
call, the assertion would be incorrect, but TypeScript would have no way of knowing that.
Upvotes: 0