Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spread between other arguments in a function call call does not work #26350

Closed
ChiriVulpes opened this issue Aug 10, 2018 · 8 comments
Closed
Assignees
Labels
Fix Available A PR has been opened for this issue Suggestion An idea for TypeScript

Comments

@ChiriVulpes
Copy link

ChiriVulpes commented Aug 10, 2018

TypeScript Version: 3.1.0-dev.20180809

Search Terms: tuple spread function call

Code

function foo (a: 1, b: 2, c: 3) { }

const bar: [1, 2] = [1, 2];

foo(...bar, 3);

Expected behavior:
No error.

Actual behavior:
On the third line: Expected 3 arguments, but got 1 or more.

Notes:
If this is intended to work this way, or is simply not implemented yet, this issue can be edited into a feature request.

First use case I can think of is my Vector2/3 classes, which support xy: [number, number] and xyz: [number, number, number]. I have some methods which require x, y, and z values, and do not want a vector (legacy code). It would be nice to be able to spread into them.

Another use pertaining to my vector example is constructing a V3 from a V2, new Vector3(...v2.xy, z)

Also, this already works if the tuple exactly matches the method call, example:

const baz: [1, 2, 3] = [1, 2, 3];
foo(...baz);
@DanielRosenwasser DanielRosenwasser added the Bug A bug in TypeScript label Aug 10, 2018
@DanielRosenwasser DanielRosenwasser added this to the TypeScript 3.1 milestone Aug 10, 2018
@ajafff
Copy link
Contributor

ajafff commented Aug 10, 2018

This seems to be related to #26058 (at least the merging part of that proposal)

@ChiriVulpes
Copy link
Author

ChiriVulpes commented Aug 10, 2018

I saw that issue but wasn't sure how related it would be, because you can't spread into tuples at all, but you can spread into function calls when the arguments exactly match the tuple.

const x: [1, 2, 3] = [1, 2, 3];
const y = [...x]; // (1 | 2 | 3)[]
function foo(a: 1, b: 2, c: 3) {}
foo(...x); // works

Guess I should have put it in the related section anyway 😛

@ahejlsberg
Copy link
Member

It is currently working as intended, so I'm changing this issue to a suggestion. The compiler only flattens tuples into parameter lists when they occur in a spread parameter in the last parameter position. When a spread parameter occurs earlier than the last parameter position, the remaining parameters are considered to just be part of the spread, i.e. foo(x, ...y, z) is checked as foo(x, ...[...y, z]).

@ahejlsberg ahejlsberg added Suggestion An idea for TypeScript and removed Bug A bug in TypeScript labels Aug 17, 2018
@RyanCavanaugh RyanCavanaugh added the In Discussion Not yet reached consensus label Aug 20, 2018
@RyanCavanaugh RyanCavanaugh removed this from the TypeScript 3.1 milestone Aug 20, 2018
@robbiespeed
Copy link

@ahejlsberg this still seems like a bug to me, it's not that uncommon to have a spread in an earlier position. As well the logic being applied seems to be affecting spread even without using tuples, see #28010

@Neonit
Copy link
Contributor

Neonit commented Mar 26, 2019

@ahejlsberg

When a spread parameter occurs earlier than the last parameter position, the remaining parameters are considered to just be part of the spread, i.e. foo(x, ...y, z) is checked as foo(x, ...[...y, z]).

I don't quite understand the problem or difference here. If y is a tuple of known fixed length 2 with types ty1 and ty2, then [...y, z] should be identifiable as tuple of known fixed length 3 with types ty1, ty2 and tz. In that case the signature is known and can be checked accordingly, no matter where the spread occurs.

What am I missing?

@ahejlsberg
Copy link
Member

What am I missing?

It certainly is possible to do what you're suggesting, but it adds more complexity to the compiler logic that reasons about the effective argument list (i.e. the "flattened" argument list). Also, it assumes that the fixed length of the tuple can be trusted at run-time. For example, treating a longer array as a fixed length tuple doesn't lead to issues when it is in the last position, but would cause alignment bugs when more arguments follow.

@murphyke
Copy link

murphyke commented Jul 2, 2020

I also have a use case for this in existing code that I am porting to TS: f(...tupleMaker(1), ...tupleMaker(2)). The error is "Expected 4 arguments, but got 1 or more. An argument for 'firstArg' was not provided."

@ahejlsberg
Copy link
Member

This issue was fixed by #39094 which is included in the 4.0 beta. Playground link:

https://www.typescriptlang.org/play/index.html?ts=4.0.0-beta#code/GYVwdgxgLglg9mABMOdEAoCGAuRBGAGkQCNcAmIiXAZgEpEBvRAXwFgAoDiBAZyhMwAnXAG1CiMgF1EAXkRiiUgNwcOKOOgB024kKJ0V7IA

@ahejlsberg ahejlsberg added Fix Available A PR has been opened for this issue and removed In Discussion Not yet reached consensus labels Jul 2, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Fix Available A PR has been opened for this issue Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

8 participants