Home > Blockchain >  Typescript classes constructor order
Typescript classes constructor order

Time:07-05

I have a class that I initialize with a token.

This class then initializes another class to which it should pass this token.

I don't understand why the second class constructor is called before the main class constructor.

Is there any way to reverse the order?

Thanks.

// Main class
export class MainClass {

  token: string;

  constructor(t: string) {
    console.log("MainClass constructor", t);

    this.token = t;

  }
  private secondClass: SecondClass = new SecondClass(this.token);
}

// Second class
export class SecondClass {
  constructor(t: string) {
    console.log("SecondClass constructor",t);
  }
}


// Client code
const app = new MainClass("1234");

// Console
--> "SecondClass constructor undefined"
--> "MainClass constructor 1234"

CodePudding user response:

There's a little bit of a wrinkle with this question, since class fields existed in TypeScript before they were finally integrated into the ES2022 spec, and depending on your compiler settings (such as the --useDefinedForClassFields compiler option) the compiled JavaScript might have somewhat different behavior. But I think all of versions have the same initialization order.

In the MDN documentation for class fields it says:

Public instance fields are added ... either at construction time in the base class (before the constructor body runs), or just after super() returns in a subclass.

That means you can expect class fields to be initialized before the code in the constructor body. And therefore your code is more or less the same as:

function MainClass(t) {
    this.secondClass = new SecondClass(this.token);
    console.log("MainClass constructor", t);
    this.token = t;
}

or maybe

function MainClass(t) {
    Object.defineProperty(this, "token", {
        enumerable: true,
        configurable: true,
        writable: true,
        value: void 0
    });
    Object.defineProperty(this, "secondClass", {
        enumerable: true,
        configurable: true,
        writable: true,
        value: new SecondClass(this.token)
    });
    console.log("MainClass constructor", t);
    this.token = t;
}

And either way, new SecondClass(this.token) runs before this.token is set to t.


The way around this is to initialize the properties inside the constructor body explicitly. This lets you have more control over the order:

class MainClass {
    token: string;
    constructor(t: string) {
        console.log("MainClass constructor", t);
        this.token = t;
        this.secondClass = new SecondClass(this.token);
    }
    private secondClass: SecondClass;
}

Which should result in the expected:

[LOG]: "MainClass constructor",  "1234" 
[LOG]: "SecondClass constructor",  "1234" 

Playground link to code

  • Related