Typescript: Naming arrow functions

Created on 11 Jan 2016  Â·  37Comments  Â·  Source: microsoft/TypeScript

Hi there,

Not sure whether this has been already mentioned before (I would be surpirsed if it never was)... Wouldn't it be a good idea to name arrow functions, which is what apparently ES6 does?. Think about the benefits of this in the stacktrace. It's quite useful to have a "named" function in there, instead of an anonymous one ;-)

So, this:

const double = x => x * 2;

would be transpiled down to:

var double = function double(x) {
  return x * 2;
};

Just to mention that Babel does it like that ;-)

Thanks.

Bug ES6 help wanted

Most helpful comment

So I'm using typescript 3.6 with node v12.10.0 and targeting es2018 with module commonjs.
this works:

const foo = () => ({ foo: 'foo' })
export { foo }
console.log(foo.name)

This doesn't:

export const foo = () => ({ foo: 'foo' })
console.log(foo.name)

directly exporting a const changes the name behaviour

All 37 comments

This would also be nice to have for arrow functions used as class methods/properties.

private handleDocumentClick = (...) => {
    ...
}

Wouldn't it be a good idea to name arrow functions, which is what apparently ES6 does?

Can you clarify this part?

This would also be nice to have for arrow functions used as class methods/properties.

Note that we can't safely name class methods -- a reference to handleDocumentClick in the method body must refer to whatever that name means in the enclosing lexical scope, not the function itself.

Consulted with @bterlson and we think this is a spec-affecting behavior, specifically the places where the abstract SetFunctionName operation is invoked https://tc39.github.io/ecma262/#sec-setfunctionname

Regular function expressions have very noncomplaint behavior in current runtimes and should probably just be left alone (specifically, Edge, Chrome, and Node all produce an own property of name on functions, when they shouldn't)

Hi @RyanCavanaugh, answering your question... as far as I know in ES6 if an arrow function is defined on the right-hand-side of an assignment expression, the ‎engine will take the name on the left-hand-side and set the arrow function's ".name" based on that

Thanks

Hi @RyanCavanaugh, could you explain the SetFunctionName operation? I couldn't fully grasp why it should be an issue.

I tried this in chrome.

let l = {log(){}}
console.log(l.log.name) //outputs 'log'

Generated typescript:

var l = { log: function () { } };
l.log.name // empty

So it seems under the covers v8 makes nice named anonymous functions.

Tried the following in the chrome dev console. Was surprised :rose:

let foo = function(){}
foo.name; // "foo"

image

@basarat It didn't happen for me in v50.0.2661.102m, but I just upgraded to v51.0.2704.63m and now it's doing that.

PRs welcomed

I will attempt to make a PR in following days or weeks, depending on how difficult it turns out to be. ;)

@mhegazy For clarification - should PR for this issue fix all naming issues according to ES6 spec (naming arrow function object properties etc...), or only this one case of const x = () => {}?

Also, should it only target arrow functions or regular anonymous functions - function () {} - as well?

Also, is it ok to open work in progress PR to make sure changes go in good direction and get some help from community (as a newbie contributor)?

I would say all lambdas/function expressions should behave as such. feel free to send us incomplete PRs if you want to get early feedback. please tag @rbuckton on your PRs.

See also: #6757.

I notice the PR hasn't been merged, is anybody working on this?

I worked on it for some time but I am currently not.

using TypeScript 2.3.3, issue hasn't been solved, #6757 is a dup.

@mpodlasin Would you have some pointers on where to start with this issue? I would love to fix it as the name of functions are valuable when building devtools.

No, my approach was not good as you will see in review of my PR: https://github.com/Microsoft/TypeScript/pull/16001

I did not work on it after that.

Good luck!

For symbols the function name would be wrapped in brackets:

const sym = Symbol('SYMBOL_NAME')
const obj = { [sym]: () => 42 }
console.log(obj[sym].name) // [SYMBOL_NAME]

As long as arrow function as property being respected, it may need adding runtime helpers to properly setting the name per spec.

My issue #33408 was marked as a duplicate and closed. I have a working repro here where native JavaScript vs JavaScript compiled from TypeScript produces different functionality:

git clone https://github.com/sabrehagen/tsc-function-repro
yarn --cwd tsc-function-repro test

Can we get an ETA on the resolution of this bug? There have been at least 6 github issues created since January 2016 regarding it, and it's still unresolved. Would be great to get some clarity on likelihood of resolution as our production code is affected by this functional inconsistency.

I cannot believe this is still happening after I reported this issue almost 4 years ago. Maybe we need a reminder:

Typescript today:
Screenshot 2019-09-24 at 11 07 54

Babel, at least 4 years ago:
Screenshot 2019-09-24 at 11 07 36

Chrome since arrow functions were introduced:
Screenshot 2019-09-24 at 11 11 52

In case it isn't clear, anyone's free to send a PR when an issue is in the Backlog milestone. Otherwise we could probably do this for 3.8; there are a lot of bugs in the 3.7 milestone already.

So I'm using typescript 3.6 with node v12.10.0 and targeting es2018 with module commonjs.
this works:

const foo = () => ({ foo: 'foo' })
export { foo }
console.log(foo.name)

This doesn't:

export const foo = () => ({ foo: 'foo' })
console.log(foo.name)

directly exporting a const changes the name behaviour

@BrendanBall if you’re targeting es2018 then TS may not even be transpiling and letting node handle the arrow function.

This is really about how TS down transpiles arrow functions.

If I am correct this fix would be in the emit stage. Arrow functions need to know if they are assigned to a const. If so, emit with the name of the const

@nojvek You are correct that TS isn't transpiling the arrow function, however it's still the TS transpilation that breaks the naming for me and the end result is the same, my arrow functions aren't named when they should be. Shall I open a separate issue for this? In my case it's breaking because of how export const is being transpiled to commonjs.

image

This is the main reason that this change would be useful to me, since this is my React devtools at the moment, at least without manually setting .displayname.

@OfficerHalf doesn't react use babel for compilation anyway? So what TS transpilation looks like should not really matter..

@DanielRosenwasser In order to fix this babel mangles the variable names beyond recognition (ex).

Wouldn't a partial fix here for functions that do not reference themselves in the body be good enough ? Adding a name to those will not cause (*) any shadowing issues and it will cover most common scenarios while not mangling the output.

On the other hand this solution will require an extra pass over all arrow function to determine if they do reference themselves, which may impact performance so it might just make sense to label this as will not fix and closing it 🤷‍♀️

_(*) as far as I can tell_

Sure, if you use CRA it uses babel. But you don't have to - if you use "jsx": "react" in your tsconfig, typescript will handle that part of the compilation. In this particular project we don't use babel.

I'm inclined to agree with @dragomirtitian that we should just Won't Fix this due to the rapidly-evaporating ecosystem of ES5-only runtimes. The actual impact here is quite minimal compared to the sheer ugliness of the required emit.

IE 11 isn’t vanishing particularly rapidly.

Agreed - over 50% of traffic I see in my particular industry comes from IE11. We can't deprecate it yet since some customers are unable to upgrade even if they wanted to.

Would it be acceptable if the names were set to something "sufficiently close" to the original name? The point in that being that it would make debugging easier even if it's not the correct behavior. So in

const foo = () => {
}

we would rewrite that to

var foo = function foo_1() {
}

while that's certainly better than outputting no name at all, or a mangled name, that seems like a short-term workaround pending an actual fix.

Why would A be easier than the desired B?

  • A (proposed workaround)
var foo = function foo_1() {
}
  • B (desired)
var foo = function foo() {
}

@basarat Picking a name that does not conflict with anything else would remove the need to rewrite variable names if the body of the function references the the variable.

Ex:

var x;
x = recursive => {
    console.log("original x")
    return x(true); // The function assigned to x below is actually called
}
var y = x;
x = function () {
  console.log("new x")
}
y(true)

If you give the first function the name x, then the body of the function will see the function itself as x not the variable x (which is reassigned later) which will make the behavior different to the original code in ES2015 and above.

So you end up having to do a lot of variable name rewrites, which get pretty ugly pretty fast: ex

Variable names aren’t observable, however, while function names are.

Can someone implement this please?

Was this page helpful?
0 / 5 - 0 ratings