I have a class A
, and a class B
inherited from it.
class A { constructor(){ this.init(); } init(){} } class B extends A { private myMember = {value:1}; constructor(){ super(); } init(){ console.log(this.myMember.value); } } const x = new B();
When I run this code, I get the following error:
Uncaught TypeError: Cannot read property 'value' of undefined
How can I avoid this error?
It's clear for me that the JavaScript code will call the init
method before it creates the myMember
, but there should be some practice/pattern to make it work.
TypeScript supports single inheritance and multilevel inheritance. We can not implement hybrid and multiple inheritances using TypeScript. The inheritance uses class-based inheritance and it can be implemented using extends keywords in typescript.
We inherit constructor functionality with the super() constructor. If the parent class constructor has any parameters, the super() constructor will need the same parameters.
In TypeScript there are two ways to do this. The first option is to cast the object to any . The problem with this option is that you loose type safety and intellisense autocompletion. The second option is the intentional escape hatch.
Constructors are not members, so they are not inherited by subclasses, but the constructor of the superclass can be invoked from the subclass.
This is why in some languages (cough C#) code analysis tools flag usage of virtual members inside constructors.
In Typescript field initializations happen in the constructor, after the call to the base constructor. The fact that field initializations are written near the field is just syntactic sugar. If we look at the generated code the problem becomes clear:
function B() { var _this = _super.call(this) || this; // base call here, field has not been set, init will be called _this.myMember = { value: 1 }; // field init here return _this; }
You should consider a solution where init is either called from outside the instance, and not in the constructor:
class A { constructor(){ } init(){} } class B extends A { private myMember = {value:1}; constructor(){ super(); } init(){ console.log(this.myMember.value); } } const x = new B(); x.init();
Or you can have an extra parameter to your constructor that specifies whether to call init
and not call it in the derived class as well.
class A { constructor() constructor(doInit: boolean) constructor(doInit?: boolean){ if(doInit || true)this.init(); } init(){} } class B extends A { private myMember = {value:1}; constructor() constructor(doInit: boolean) constructor(doInit?: boolean){ super(false); if(doInit || true)this.init(); } init(){ console.log(this.myMember.value); } } const x = new B();
Or the very very very dirty solution of setTimeout
, which will defer initialization until the current frame completes. This will let the parent constructor call to complete, but there will be an interim between constructor call and when the timeout expires when the object has not been init
ed
class A { constructor(){ setTimeout(()=> this.init(), 1); } init(){} } class B extends A { private myMember = {value:1}; constructor(){ super(); } init(){ console.log(this.myMember.value); } } const x = new B(); // x is not yet inited ! but will be soon
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With