miguelcg
miguelcg

Reputation: 21

Typescript class constructor generated code order

I am writing a service and I am getting the following error when running it. The problematic part is:

import { Configuration } from "./Configuration";
import { BooksApplication } from "./application/BookApplication";
import { PersistenceBookRepository } from "./application/PersistenceBookRepository";
import { DatabaseAccessor } from "./database/DatabaseAccessor";

export class Context {
  public readonly configuration: Configuration;

  public constructor(configuration: Configuration) {
    this.configuration = configuration;
  }

  private readonly databaseAccessor = new DatabaseAccessor(
    this.configuration.databaseConfiguration
  );

  private readonly bookRepository = new PersistenceBookRepository(
    this.databaseAccessor
  );

  private readonly bookService = new BooksApplication(this.bookRepository);
}

And the error:

TypeError: Cannot read property 'databaseConfiguration' of undefined
    at new Context (/sandbox/src/Context.ts:14:24)
    at Object.<anonymous> (/sandbox/src/index.ts:6:17)
    at Module._compile (internal/modules/cjs/loader.js:689:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)
    at Module.load (internal/modules/cjs/loader.js:599:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:538:12)
    at Function.Module._load (internal/modules/cjs/loader.js:530:3)
    at Function.Module.runMain (internal/modules/cjs/loader.js:742:12)
    at startup (internal/bootstrap/node.js:283:19)
    at bootstrapNodeJSCore (internal/bootstrap/node.js:743:3)
error Command failed with exit code 1.

I have check the generated code from Typescript and it looks like this:

// ...
var Context = (function () {
    function Context(configuration) {
        this.databaseAccessor = new DatabaseAccessor_1.DatabaseAccessor(this.configuration.databaseConfiguration);
        this.bookRepository = new PersistenceBookRepository_1.PersistenceBookRepository(this.databaseAccessor);
        this.bookService = new BooksApplication_1.BooksApplication(this.bookRepository);
        this.configuration = configuration;
    }
    return Context;
}());
// ...

As you can see this.configuration = configuration; is at the end so this.databaseAccessor = new DatabaseAccessor_1.DatabaseAccessor(this.configuration.databaseConfiguration); fails.

Am I doing something wrong or there is a bug in Typescript?

I have tried to change the order of the class members but same result.

I have a project reproducing the issue here: https://codesandbox.io/embed/644k5kwr4r

Thanks.

Upvotes: 0

Views: 151

Answers (1)

Jonas Wilms
Jonas Wilms

Reputation: 138307

That is actually the right initiation order according to the spec proposal¹:

When field initializers are evaluated and fields are added to instances:

  • Base class: At the beginning of the constructor execution, even before parameter destructuring.

  • Derived class: Right after super() returns. (The flexibility in how super() can be called has led many implementations to make a separate invisible initialize() method for this case.)

TLDR: class fields are executed first, before the constructors body.

You could just move the initialization into the constructor though.


¹ as TS is a superset of JS, it has to be compliant to the ES spec too.

Upvotes: 1

Related Questions