Skip to content

Excess spread arguments are not errored #19900

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

Closed
kitsonk opened this issue Nov 10, 2017 · 11 comments
Closed

Excess spread arguments are not errored #19900

kitsonk opened this issue Nov 10, 2017 · 11 comments
Assignees
Labels
Bug A bug in TypeScript Fixed A PR has been merged for this issue

Comments

@kitsonk
Copy link
Contributor

kitsonk commented Nov 10, 2017

TypeScript Version: 2.6.1

Code

It appears that extra arguments that are spread arguments are always accepted, which can lead to unintentional errors in the code. For example the following:

declare const foo: (value: string) => string;

foo('bar', 'qat'); // Expected 1 arguments, but got 2.

foo('bar', ...['qat']); // No error

This is related to #4130 but @sandersn suggested a separate issue, as this subset of the larger issue is likely addressable, where as determining if a spread argument might result in longer arguments is difficult, versus cases where it is easily statically identifiable (this case).

Expected behavior:

That an extra spread argument errors.

Actual behavior:

An excess spread argument does not error.

@mhegazy mhegazy added the Bug A bug in TypeScript label Nov 10, 2017
@mhegazy mhegazy added this to the TypeScript 2.7 milestone Nov 10, 2017
@sandersn
Copy link
Member

When I tried to fix this, I found the test that explains why it works this way. It's for functions that use arguments:

function mixed(fixed: number) {
    Debug.assert(fixed > 0);
    for (var i = fixed; i < arguments.length; i++) {
        console.log(arguments[i]);
    }
}
mixed(2, 'hello', 'world', 'goodbye', 'goodbye') // ok ... I guess?

I guess this makes sense, but I think the right thing to do is only allow calls like this for functions that use arguments in the body and disallow it for all other calls.

@mhegazy
Copy link
Contributor

mhegazy commented Nov 13, 2017

is that in a .js file?

@sandersn
Copy link
Member

The test is a .ts file; I haven't tracked the comment in the test back to a discussion on github yet. Maybe restricting that usage to javascript would be an acceptable solution too.

@mhegazy
Copy link
Contributor

mhegazy commented Nov 13, 2017

The call is an error in TS if you do not have the spread. i.e. mixed(2, 'hello', 'world', 'goodbye', 'goodbye'). the function should have been declred as:

function mixed(fixed: number, ...args: string[]) {
    Debug.assert(fixed > 0);
    for (var i = fixed; i < arguments.length; i++) {
        console.log(arguments[i]);
    }
}

or

function mixed(fixed: number, ...args: string[]);
function mixed(fixed: number) {
 Debug.assert(fixed > 0);
    for (var i = fixed; i < arguments.length; i++) {
        console.log(arguments[i]);
    }
}

@sandersn
Copy link
Member

Ah, I see. Yes, the argument has to be a spread, which is what @kitsonk actually has in his code base. I mistakenly converted it to individual arguments.

Here's the actual test case from cases/conformance/expressions/functionCalls/callWithSpread2.ts:

declare function normal(s: string): void;
declare numbers: number[];
normal("i", ...numbers);

I think this should be disallowed. The question is whether it should be allowed if this is the definition of normal:

function normal(s: string) {
  console.log(s);
  console.log(arguments[1]);
}

@mhegazy
Copy link
Contributor

mhegazy commented Nov 14, 2017

That looks wrong.. i would be interested to see if any RWC/user code test fails because of that.

@kitsonk
Copy link
Contributor Author

kitsonk commented Nov 14, 2017

function normal(s: string) {
  console.log(s);
  console.log(arguments[1]);
}

That is exactly the sort of JS bs TypeScript should help us fix as well... I suspect it will break some user code because if you are porting some code over, and TypeScript didn't force you to change it, you certainly wouldn't fix it... I guess it is arguable if it should be strict or not, since it would be a breaking change, but it seems like forcing someone to add an argument (or a spread argument) to strongly type their arguments is worth it for the sanity of code.

@sandersn
Copy link
Member

@kitsonk that’s a good argument. Unfortunately I didn’t record my previous reasoning in the PR #15849. I think it should be fine to make this an error, although I doubt our test suites will catch anything since this has only been legal since May.

@sandersn
Copy link
Member

Nope, no failures from RWC or user code.

@sandersn
Copy link
Member

Here's a counter-example that I thought about and then decided should still be illegal:

function f() { return arguments[0] }
declare var ns: number[];
f(...ns)

By analogy with what we allow in Javascript:

function j() { return arguments[0] }
j(1,2,3)

However, if you're using Typescript, or a version of Javascript that supports ... spread arguments, you also have ... rest parameters, and should always write f as function f(...args) { return args[0] }.

@sandersn
Copy link
Member

Fix is up at #20071

@mhegazy mhegazy added the Fixed A PR has been merged for this issue label Nov 20, 2017
@microsoft microsoft locked and limited conversation to collaborators Jun 14, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Bug A bug in TypeScript Fixed A PR has been merged for this issue
Projects
None yet
Development

No branches or pull requests

3 participants