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

Comments

Projects
None yet
9 participants
@DanielRosenwasser
Member

DanielRosenwasser commented Jan 6, 2015

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;
}
@Arnavion

This comment has been minimized.

Show comment
Hide comment
@Arnavion

Arnavion Jan 14, 2015

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."

Contributor

Arnavion commented Jan 14, 2015

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

This comment has been minimized.

Show comment
Hide comment
@yuit

yuit Jan 27, 2015

Contributor

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.

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

This comment has been minimized.

Show comment
Hide comment
@RyanCavanaugh

RyanCavanaugh Jan 28, 2015

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.

Member

RyanCavanaugh commented Jan 28, 2015

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

This comment has been minimized.

Show comment
Hide comment
@yuit

yuit Jan 29, 2015

Contributor

This issue has been cooperated into PR #1627

Contributor

yuit commented Jan 29, 2015

This issue has been cooperated into PR #1627

@yuit yuit closed this Feb 6, 2015

@mhegazy mhegazy added the Fixed label Feb 7, 2015

@basarat

This comment has been minimized.

Show comment
Hide comment
@basarat

basarat Mar 18, 2015

Contributor

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 ❤️

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

This comment has been minimized.

Show comment
Hide comment
@DanielRosenwasser

DanielRosenwasser Mar 21, 2015

Member

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

Member

DanielRosenwasser commented Mar 21, 2015

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

@robertpenner

This comment has been minimized.

Show comment
Hide comment
@robertpenner

robertpenner Apr 19, 2015

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.

robertpenner commented Apr 19, 2015

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

This comment has been minimized.

Show comment
Hide comment
@basarat

basarat Apr 19, 2015

Contributor

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

Contributor

basarat commented Apr 19, 2015

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

@mhegazy

This comment has been minimized.

Show comment
Hide comment
@mhegazy

mhegazy Apr 20, 2015

Contributor

Or use rest args:

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

Instead of

() => arguments[0]

Contributor

mhegazy commented Apr 20, 2015

Or use rest args:

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

Instead of

() => arguments[0]

@addityasingh

This comment has been minimized.

Show comment
Hide comment
@addityasingh

addityasingh 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.

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.