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"