Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Access child properties from parent constructor

Tags:

typescript

Assume the following Typescript example

class Animal {
    public name: string;

    constructor() {
        console.log(this.name);
    }
}

class Snake extends Animal {
    public name = 'BeatSSS';
}

let someSnake = new Snake();

Of course, console.log() logs undefined. And BeatSSS was expected.

How can we do things from parent constructor() with child properties?

Actual ideas:

  • super() call on child construct() solve the problem, but I need do it on every child. Not very elegant solution.
  • using a timeout on parent construct logs the correct value, but is not possible use the instance of class immediately (beacouse timeout is not executed yet).
like image 902
pablorsk Avatar asked Oct 21 '17 18:10

pablorsk


1 Answers

I like what Jeff and Duncan already have proposed quite a lot. Let me show one more option, though.

abstract class Animal {
    constructor(public name: string) {
        console.log(this.name);
    }
}

class Snake extends Animal { }

let someSnake = new Snake("Snakeeeey!");

Notice a few things here.

  • The Animal class seems to be indeed an abstract concept. You have an option of either marking it as abstract or defining it as an interface, but the latter will not let you have any implementation (including constructor).
  • TypeScript allows us to define fields which are being initialized from constructor. In case of the public property we simply write constructor(public name: string).
  • The Snake class does not explicitly define a constructor. Nevertheless, when creating a snake object, the consumer has to provide the name argument that is required by Animal class.

    In Jeff's code the Snake class has a default value for the name which is achieved by constructor(name: string = 'BeatSSS') { super(name); }. I don't know your specific use case. If there's no any meaningful default name for the snake, I would avoid declaring such constructor altogether.

  • Duncan correctly explains that calling super() is not inherently wrong. Moreover, it is necessary.

    When an object of type Snake is being created by the means of its constructor, the parent (Animal's ) constructor has to complete first. Therefore, there is no way for Animal constructor to "see" the effects of field initialization that happens in the child (Snake's) constructor. That is impossible due to the order of constructor invocations.

    Thus, the only option for the child type to pass the information for parent's constructor is to call super(...) directly.


super() call on child construct() solve the problem, but I need do it on every child. Not very elegant solution.

Basically not defining a child constructor altogether is the answer. If your children class have to have the constructors they will force you to invoke super() as the very first instruction.

using a timeout on parent construct logs the correct value, but is not possible use the instance of class immediately (beacouse timeout is not executed yet).

This is a very bad option. First, it's generally not recommended to have any asynchronous code invocations in constructor.

Second, if you still do a setTimeout(() => this.fixMyState(), 0) in Animal constructor it results in a bad effect: immediately after object construction, the object is in a bad state. What if some code uses that object immediately? The () => this.fixMyState(), 0 will not even have a chance to run for repairing the object. It is a rule of thumb to have an object in a good state immediately after its construction. Otherwise, the constructor should throw an error so that no code can attempt to operate on the object in a bad state.

like image 134
Igor Soloydenko Avatar answered Sep 28 '22 03:09

Igor Soloydenko