Skip to content

Bug: __runInitializers(this) is emitted before super() call #53204

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

Closed
chharvey opened this issue Mar 11, 2023 · 0 comments · Fixed by #53268
Closed

Bug: __runInitializers(this) is emitted before super() call #53204

chharvey opened this issue Mar 11, 2023 · 0 comments · Fixed by #53268
Assignees
Labels
Bug A bug in TypeScript Fix Available A PR has been opened for this issue

Comments

@chharvey
Copy link

chharvey commented Mar 11, 2023

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 and v5.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.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Fix Available A PR has been opened for this issue
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants