jmls
jmls

Reputation: 2969

I can have either `super` or this, but not both?

I've been tearing what little of my hair out with Typescript (2.1) and classes, in particular a class that extends another class.

if I declare a function as

foo = () => {}

then I can safely use this in the function

but I can't use super.foo(), as I get the error

TypeError: (intermediate value).foo is not a function

if I declare the function as

foo() { }

then I can use super.foo() but I can't be sure that this is pointing to the same "this" all the time, depending on how the function is being called

how do I get round this problem ? I've seen some suggestions that I have to double-declare all the functions and bind one to the other, but that seems ridiculous .. Doesn't typescript have some "magic" syntax sugar that coats all of this ugliness ?

thanks ;)

Upvotes: 2

Views: 107

Answers (1)

Ryan Cavanaugh
Ryan Cavanaugh

Reputation: 220954

Class methods can either be on the instance or the prototype. You can't have your cake and eat it too, unfortunately.

When a method is on the instance, you get one closure per method per class instance, and each instance's closure has its own copy of _this that makes it so the function can be arbitrarily invoked. But because the methods are bound to the instance, an instatation of the derived class does not create any extant method with the base class's method code.

When a method is on the prototype, you get one closure per method, and those methods are unbound and therefore must be called with the correct method syntax. Here you can invoke base class implementations because there's a known location to get the base class method from (BaseClass.prototype.methodName).

A good solution is to write a protected static base class method which handles the logic that foo would have invoked, and let your instance methods defer to the static method. This is roughly the best of both worlds at the cost of one level of indirection.

Example:

class Base {
    protected static foo(instance: Base) {
        instance.counter++;
    }

    counter = 0;

    foo = () => Base.foo(this);
}

class Derived extends Base {
    foo = () => {
        Base.foo(this);
        this.counter *= 2;
    }
}

Upvotes: 1

Related Questions