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

Emit of 'arguments' in arrow functions is incorrect per ES6 spec. #1609

Closed
DanielRosenwasser opened this issue Jan 6, 2015 · 10 comments
Closed
Labels
Breaking Change Would introduce errors in existing code Bug A bug in TypeScript Fixed A PR has been merged for this issue

Comments

@DanielRosenwasser
Copy link
Member

From 9.2.13 of the ES6 Spec Draft:

a. NOTE Arrow functions never have an arguments objects.

In fact, we should be capturing arguments from the first containing function expression and error if there is none. The correct emit for something like

function f() {
    return () => arguments;
}

would be the following:

function f() {
    var _arguments = arguments;
    return () => _arguments;
}
@DanielRosenwasser DanielRosenwasser added Bug A bug in TypeScript Breaking Change Would introduce errors in existing code labels Jan 6, 2015
@sophiajt sophiajt added this to the TypeScript 1.5 milestone Jan 12, 2015
@Arnavion
Copy link
Contributor

As a practical point, both FF and V8 (with io.js --harmony_arrow_functions) have the same incorrect behavior as of now.

function foo() { return () => arguments[0]; } foo(5)(6) // 6

(Unchanged in strict mode.)

jstransform also does not capture the outer arguments object I think, but 6to5 does.

The spec has finalized on this point for more than a year now, so perhaps it's okay to go ahead with this and expect ES6-supporting browsers to fix their implementation. But perhaps the ES6 emit could implement a warning for this? "Warning - using an unbound arguments inside this arrow function may not behave as you expect. Consider explicitly binding the outer arguments object, or use a splat on the arrow function."

@yuit
Copy link
Contributor

yuit commented Jan 27, 2015

After discussing with @RyanCavanaugh, we've come to the decision that we should avoid changing the semantics of existing code and to give an error so as to push users away from using this construct. However, we will still emit so users will still be able to get this behavior if they desire.

@RyanCavanaugh
Copy link
Member

This probably deserves some documentation since I'm guessing this will hit a few people.

What is the change?

It is now an error to reference the implicit arguments variable inside an arrow function.

Why?

The ECMAScript 6 specification says that no arguments variable is created for an arrow function. This means that in an arrow function inside a regular function, arguments refers to the outer function's arguments.

Prior to the ES6 compile target, the TypeScript compiler would emit an arrow function as a regular function, and access to the arguments variable was allowed. The result is that arrow functions in pre-ES6 emitted code would be using the inner arguments variable.

If ES3/ES5-targeted code that used arguments in an arrow function were changed to emit to ES6 and run on a compliant runtime, the behavior of those functions would change.

Notably, several runtimes today do not respect this aspect of the ES6 spec and will incorrectly provide the inner function arguments as arguments.

Our options

When faced with this problem, we had a few choices:

1. Rewrite arguments like we do for this. We could change downlevel (ES3/ES5) arrow function emit to capture the outer arguments variable (same as the compiler does for this today) and emit ES6 arrow functions normally. This was considered too dangerous as it would change the semantics of existing code in dangerous ways.

2. Always downlevel-emit arrow functions. We could choose to not emit native arrow functions in ES6. This would preserve the non-compliant ES3/ES5 behavior where arguments refers to the arrow function's arguments. However, this would effectively fork TypeScript from JavaScript, making it impossible to safely transition ES6 code to TypeScript while preserving semantics. This is not an option.

3. Error today, maybe do option 1 later. This is where we've landed. Any TypeScript code that uses arguments today in an arrow function is necessarily expecting different behavior than what will eventually be supported in ES6 runtimes. Additionally, because many ES6 runtimes do not correctly handle arguments in arrow functions, it's not really safe to use arguments at all until the runtimes and the ES6 specification come into alignment. Code that wants to use arguments from the outer function should be capturing them (e.g. var args = arguments; in the outer function) so that they get the same behavior in compliant and non-compliant runtimes.

Once we're confident everyone has upgraded their code and sorted out the errors, and once the JavaScript runtimes start implementing the correct behavior, we can probably remove this error and start doing arguments rewriting in downlevel emit if it becomes a common pattern. This is likely several years away, practically speaking.

@yuit
Copy link
Contributor

yuit commented Jan 29, 2015

This issue has been cooperated into PR #1627

@yuit yuit closed this as completed Feb 6, 2015
@mhegazy mhegazy added the Fixed A PR has been merged for this issue label Feb 7, 2015
@basarat
Copy link
Contributor

basarat commented Mar 18, 2015

The 'arguments' object cannot be referenced in an arrow function. Consider using a standard function expression.

^ because I want this to show up in google search results ❤️

@DanielRosenwasser
Copy link
Member Author

Hold on there, let's not forget the TypeScript diagnostic code, TS9002.

@robertpenner
Copy link

I'm getting code TS2496 in TypeScript 1.5 alpha:

TS2496: The 'arguments' object cannot be referenced in an arrow function. Consider using a standard function expression.

@basarat
Copy link
Contributor

basarat commented Apr 19, 2015

@robertpenner by design. Just use "function" instead of "()=>"

@mhegazy
Copy link
Contributor

mhegazy commented Apr 20, 2015

Or use rest args:

(...args) => args[0]

Instead of

() => arguments[0]

@addityasingh
Copy link

addityasingh commented Apr 20, 2018

@basarat I get the same error even with function. I want to throw an error to validate that the minimum number of arguments are passed to a function, like below

function validateArgsCount(foo, bar, baz) {
  if(arguments.length !== 3) {
     throw new Error('Function expects exactly 3 arguments');
  }
}

with error

The 'arguments' object cannot be referenced in an async function or method in ES3 and ES5

But with TSC version 2.8.1.

@microsoft microsoft locked and limited conversation to collaborators Jul 30, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Breaking Change Would introduce errors in existing code Bug A bug in TypeScript Fixed A PR has been merged for this issue
Projects
None yet
Development

No branches or pull requests

9 participants