Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript: can not access member value in inherited class constructor

Tags:

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.

like image 900
Adam Avatar asked Apr 11 '18 12:04

Adam


People also ask

Does TypeScript allow inheritance?

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.

Is constructor inherited 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.

How do I access private properties in TypeScript?

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.

Can you inherit a constructor?

Constructors are not members, so they are not inherited by subclasses, but the constructor of the superclass can be invoked from the subclass.


1 Answers

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 inited

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  
like image 84
Titian Cernicova-Dragomir Avatar answered Sep 20 '22 20:09

Titian Cernicova-Dragomir