-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Description
Bug Report
🔎 Search Terms
Typescript 5, decorator, emit, __runInitializers, super, constructor
🕗 Version & Regression Information
Version v5.1.0-dev.20230310 (nightly)
- This changed between versions
v4.9.5andv5.0.0-rc - I was unable to test this on prior versions because the new decorator spec was not supported
⏯ Playground Link
Playground link with relevant code
💻 Code
class Foo {}
class Bar extends Foo {
public constructor() {
console.log('Entering constructor.');
super();
console.log('Exiting constructor.');
}
@loggedMethod
public greet(): void {
console.log('Hello!');
}
}
function loggedMethod(method: any, _context: any): any {
return function (this: any) {
console.log('Entering method.');
method.call(this);
console.log('Exiting method.');
};
}
new Bar(); /* JS Error:
Must call super constructor in derived class before
accessing 'this' or returning from derived constructor */🙁 Actual behavior
The emitted output includes the following (abbreviated for clarity; see entire output in playground):
// ...
class Bar extends Foo {
// ...
constructor() {
__runInitializers(this, _instanceExtraInitializers); // <-- here!
console.log('Entering constructor.');
super();
console.log('Exiting constructor.');
}
greet() {
console.log('Hello!');
}
}__runInitializers(this, _instanceExtraInitializers); was emitted before the call to super(); in the constructor. This is a problem because this cannot be referenced before super() is called. The following error is thrown:
Must call super constructor in derived class before accessing 'this' or returning from derived constructor.
🙂 Expected behavior
I expect the call to __runInitializers to be emitted after super:
constructor() {
console.log('Entering constructor.');
super();
__runInitializers(this, _instanceExtraInitializers);
console.log('Exiting constructor.');
}🔄 Workaround
Add a private field. Private fields are initialized after super(), so the location where __runInitializers is emitted isn’t a problem.
Source:
class Bar extends Foo {
readonly #HACK = 0;
public constructor() {
console.log('Entering constructor.');
super();
console.log('Exiting constructor.');
}
// ...
}Output:
// ...
class Bar extends Foo {
// ...
#HACK = (__runInitializers(this, _instanceExtraInitializers), 0);
constructor() {
console.log('Entering constructor.');
super();
console.log('Exiting constructor.');
}
// ...
}(Note/Reminder: Even though the field is written above the constructor, it isn’t executed until after the super() call.)