Reputation: 1452
I have 2 classes, A and B in Typescript. B derives from A, A's constructor calls function init
and B overrides init
.
a.ts
export default class A {
constructor() {
this.init();
}
init() {
console.log("A.init()")
}
}
b.ts
import A from "./a"
export default class B extends A {
public bx = 3;
public by = 10;
public bz = 0;
constructor() {
super();
}
init(){
this.bx = 5;
this.bz = this.by;
console.log("B.init()")
}
}
The js files compiled by tsc
were like
a.js
export default class A {
constructor() {
this.init();
}
init() {
console.log("A.init()");
}
}
b.js
import A from "./a";
export default class B extends A {
constructor() {
super();
this.bx = 3;
this.by = 10;
this.bz = 0; //even worse
}
init() {
this.bx = 5;
this.bz = this.by;
console.log("B.init()");
}
}
No surpise in a.js
. But in b.js
, we can see a line this.bx = 3;
after super()
in the constructor. While I agree it is necessary to initialise a property like this.bx = 3;
which is a declaration in the class, I simply cannot understand why in this case, it has this.bx=3;
after super()
even theoretically the compiler should be able to know this.bx
has been set and is not uninitialised from the AST. What's the consideration? It should only init a property if the property has never been assigned. In my case, I suppose it should be
import A from "./a";
export default class B extends A {
constructor() {
super();
}
init() {
this.bx = 3; // initialisation from the declaration can appear here before the first time use. It can be optimized because of the next line assignment
this.bx = 5; // first time use `this.bx`
this.by = 10; // the initialisation here is because this.by is used in the next line
this.bz = this.by;
console.log("B.init()");
}
}
Compare to c#
using System;
public class Program
{
public static void Main()
{
B b = new B();
Console.Out.Write("x: " + b.x + " y: " + b.y);
}
}
class A {
public A() {
init();
}
public virtual void init() {
}
}
class B : A {
public int x = 3;
public int y = 10;
public int z = 4;
public B() : base() {
}
public override void init() {
this.x = 5;
this.y = this.z;
}
}
You will get x: 5 y: 4
.
Upvotes: 0
Views: 562
Reputation: 120480
There are very sound reasons to ensure that your base class is fully initialized before initialization of the extended sub class happens, and this is mandated in the MDN documentation for super
:
By having your base class call an overridden method in its constructor that fiddles with properties of this
of the subclass, you've messed up the order of initialization. By violating this important requirement, any assumptions that compilers make on the back of this requirement are now broken.
Mayhem follows...
The most preferable outcome here would be a fat compilation error.
Upvotes: 1