-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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.5
andv5.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.)