0

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?

1
  • ... isn't an operator, operators can't do what ... does. Commented Apr 10, 2021 at 11:49

2 Answers 2

1

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.

Sign up to request clarification or add additional context in comments.

1 Comment

0

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.

1 Comment

ok...I understand. thank you for the answer! ...However, my original function (plus1 in example) is actually for calculating multiple vectors, especially for polygons...so it usually processes over 100 arguments ;) At least I want to suppress complie errors since there is no problem with the original function. Please let me know if there is a diffrent way to solve this problem. Thank you!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.