96

When I'm calling an overridden method from the base class constructor, I cannot get a value of a sub class property correctly.

Example:

class A
{
    constructor()
    {
        this.MyvirtualMethod();
    }

    protected MyvirtualMethod(): void
    {

    }
}

class B extends A
{
    private testString: string = "Test String";

    public MyvirtualMethod(): void
    {
        alert(this.testString); // This becomes undefined
    }
}

I would like to know how to correctly override functions in typescript.

3
  • 19
    Calling virtual method in constructor is considered a bad practice in any OOP language: google.com/search?q=virtual+method+in+constructor Commented Mar 10, 2016 at 14:28
  • 1
    I've almost missed @Flavien Volken answer below which helped me solve this problem just because it's not the 'accepted' answer (from up votes looks like it should be). please consider changing it for others to come... Commented Feb 27, 2020 at 17:53
  • @benshabatnoam that answer only answered the title of the question, which was incorrect given what was asked in the body, so I've updated the title of the question to match what was being asked. Commented May 23, 2021 at 19:50

4 Answers 4

169

The key is calling the parent's method using super.methodName();

class A {
    // A protected method
    protected doStuff()
    {
        alert("Called from A");
    }

    // Expose the protected method as a public function
    public callDoStuff()
    {
        this.doStuff();
    }
}

class B extends A {

    // Override the protected method
    protected doStuff()
    {
        // If we want we can still explicitly call the initial method
        super.doStuff();
        alert("Called from B");
    }
}

var a = new A();
a.callDoStuff(); // Will only alert "Called from A"

var b = new B()
b.callDoStuff(); // Will alert "Called from A" then "Called from B"

Try it here

Sign up to request clarification or add additional context in comments.

3 Comments

Definitely should be the 'accepted' answer (almost missed it because it's not). Thanks.
@benshabatnoam the question was asking about how to call it in the base class' constructor, which this answer doesn't answer.
22

The order of execution is:

  1. A's constructor
  2. B's constructor

The assignment occurs in B's constructor after A's constructor—_super—has been called:

function B() {
    _super.apply(this, arguments);   // MyvirtualMethod called in here
    this.testString = "Test String"; // testString assigned here
}

So the following happens:

var b = new B();     // undefined
b.MyvirtualMethod(); // "Test String"

You will need to change your code to deal with this. For example, by calling this.MyvirtualMethod() in B's constructor, by creating a factory method to create the object and then execute the function, or by passing the string into A's constructor and working that out somehow... there's lots of possibilities.

5 Comments

Aren't we here in a case where new.target could be useful ?
Are we talking about Typescript?
@LongField yes. It's showing the compiled ES5 JavaScript of the B class in order to show the order of execution.
Interesting observation: TS doesn't allow to use this in constructor before calling super(), however it IS in fact used by populating with methods of a subclass. In this way, testString is not set by the moment super's constructor is called, but will be MyvirtualMethod set! so super constructor can actually use the overridden method correctly. Abstract methods (from another answer example) in this way are also callable from super ctor, however are not physically declared in the base class.
BTW, accessing this in such overridden methods is dangerous and a bad practice, but if the method is pure/stateless – seems OK.
22

If you want a super class to call a function from a subclass, the cleanest way is to define an abstract pattern, in this manner you explicitly know the method exists somewhere and must be overridden by a subclass.

This is as an example, normally you do not call a sub method within the constructor as the sub instance is not initialized yet… (reason why you have an "undefined" in your question's example)

abstract class A {
    // The abstract method the subclass will have to call
    protected abstract doStuff():void;

    constructor(){
     alert("Super class A constructed, calling now 'doStuff'")
     this.doStuff();
    }
}

class B extends A{

    // Define here the abstract method
    protected doStuff()
    {
        alert("Submethod called");
    }
}

var b = new B();

Test it Here

And if like @Max you really want to avoid implementing the abstract method everywhere, just get rid of it. I don't recommend this approach because you might forget you are overriding the method.

abstract class A {
    constructor() {
        alert("Super class A constructed, calling now 'doStuff'")
        this.doStuff();
    }

    // The fallback method the subclass will call if not overridden
    protected doStuff(): void {
        alert("Default doStuff");
    };
}

class B extends A {
    // Override doStuff()
    protected doStuff() {
        alert("Submethod called");
    }
}

class C extends A {
    // No doStuff() overriding, fallback on A.doStuff()
}

var b = new B();
var c = new C();

Try it Here

2 Comments

This solution will you force to define this method in every child class even If it is not needed.
@Max it's the sole purpose of the abstract. I put an example in my answer for you
3

below is an generic example

    //base class
    class A {
        
        // The virtual method
        protected virtualStuff1?():void;
    
        public Stuff2(){
            //Calling overridden child method by parent if implemented
            this.virtualStuff1 && this.virtualStuff1();
            alert("Baseclass Stuff2");
        }
    }
    
    //class B implementing virtual method
    class B extends A{
        
        // overriding virtual method
        public virtualStuff1()
        {
            alert("Class B virtualStuff1");
        }
    }
    
    //Class C not implementing virtual method
    class C extends A{
     
    }
    
    var b1 = new B();
    var c1= new C();
    b1.Stuff2();
    b1.virtualStuff1();
    c1.Stuff2();

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.