-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Class name is missing in Function when es6 target is set #5386
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
Comments
It sounds like this a bug with your ES6 runtime, not a TypeScript bug. What are you running on and which version of TypeScript are you using? |
Here is what I'm running: export function Document() {
return function(objectConstructor: Function) {
console.log(objectConstructor.name);
console.log('Hello world');
}
}
@Document()
export class User {
}
console.log(User.name); and looks like the problems is in the way it generates a "class", it does it without a name: var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
function Document() {
return function (objectConstructor) {
console.log(objectConstructor.name); // when targeting es5 it gives me what I want - the name of my class. When targeting es6 it does not give anything
console.log('Hello world');
};
}
exports.Document = Document;
let User = class {
};
User = __decorate([
Document(),
__metadata('design:paramtypes', [])
], User);
exports.User = User;
console.log(User.name); the issue is here: Im using typescript@next and running this code on node 4.1.2 |
Yup that would cause this error. |
Here is some background: #2836 (comment) The issue is that classes really have two symbols, one visible inside the class, and one outside, if you are using a decorators, and the decorator changes the class constructor, this only affects the outer symbol. all accesses inside of the body of the class still bind to the original class name. the fix is to change the class into a class expression, this way we only have one symbol to bind against. but that means that Function.name is not set. the fix is to set the name property explicitly. this was the original behavior, but then due to issue with Chrome/V8 this was not working (see issue #2836) so we disabled it in #3063. if V8 has addressed the issue, we should put back the name setting. |
👍 |
Hi, @mhegazy thanks for setting #6420 as duplicate and directing me to know the underlying issue. I am struggling for it for a while. From the comment "if you are using a decorators, and the decorator changes the class constructor, this only affects the outer symbol. all accesses inside of the body of the class still bind to the original class name", I can see why the code like 'let User = class {}' generated. Currently, I didn't follow the workaround(setting the name explicitly) as I think the typescript code is correct while something wrong with the underlying transpiling mechanism and incorrect behaviour. In my scenario, I will NEVER change a class name as it will confuse others I think. To avoid further changes when the fix comes, my workaround is: I forked the typescript source code and made a minor change to let it generate code like 'let User = class User {}'. It works fine for me for now. Please let me know if it is still dangerous :-) Thanks again. |
@TheLevenCreations, yes this fix is not correct. now decorators, and methods have a different view of the class, and that can cause different problems down the road. @rbuckton should have the correct fix out shortly. |
@TheLevenCreations Classes in ES6 are specified to create two bindings for the class name, one in the scope in which the class is defined, and one inside the class body: class A {
a() { return A; }
}
let objA = new A();
A = undefined;
console.log(A === objA.a()); // false This becomes a problem for decorators, if the class decorator replaces the constructor: function logInstantiation(target) {
return class extends target {
constructor(...args) {
super(...args);
console.log("instantiated");
}
}
}
@logInstantiation
class C {
newInstance() { return new C(); }
}
let objC = new C(); // logs "instantiated";
let objC2 = objC.newInstance(); // nothing is logged as 'C' is the original class We at one point tried to address the fact that the Function#name is missing by setting the name on the result using: Object.defineProperty(C, "name", { value: "C" }); While this is perfectly legal ES6, it only works in strict mode in Node v4. Also, we saw issues when people tried to further transpile ES6 output from TypeScript in Babel. The only other approach we've considered so far is to create a temp variable and alias all references to the class inside the class body to this temp variable, effectively turning the original example above into this: // input.ts
@Document
export class User {
newInstance() { return new User(); }
}
// output.js
export class User {
newInstance() { return new _a(); }
}
User = _a = __decorate([Document], User);
var _a; |
Hi @rbuckton Anyway, hope it will be fixed soon, and I will test my code again. Thank you :-) |
I have decorator:
Here is decorator usage:
Im using "User" class name to create a document called "User", I need information how class is called. I always used es5, but now when I switched to es6 compile target I dont have information about class name anymore. Is it a bug, or it by design? If second then what is the way to get the class name?
The text was updated successfully, but these errors were encountered: