Typescript: calling super method from extended class gives type error - (intermediate value) is not a function
Asked Answered
G

1

10

I am implementing event handling in a component named StructureWindowComponent and also have an override for it in LeggerStructureWindowComponent..

In the base class (StructureWindowComponent) the event handling for blur event is as follows:

symbolCellLostFocus = (symbolField : MatInput, $index: number) =>{
    console.log("base class symbol cell lost focus");
    //implementation...
  }

In the derived class LeggerStructureWindowComponent, I am calling this method using super like this...

symbolCellLostFocus = (symbolField : MatInput, $index: number) =>{
    console.log("derived class symbol lost focus");
    super.symbolCellLostFocus(symbolField, $index);
  }

I get the error in the console: ERROR TypeError: (intermediate value).symbolCellLostFocus is not a function

Not sure what is wrong here.. can someone please advice?

Guarino answered 23/10, 2020 at 10:46 Comment(2)
I think you might need to add a semicolon behind the closing curly braces. Btw., is there a reason why you don't make these named functions?Isologous
Gunnar, I have just shown small part of the code. There is more logic after that. Thanks for the suggestion thoughGuarino
I
20

This is a tricky one, but it has to do with the use of arrow functions syntax in classes, and the prototype chain. It is not related to Angular specifically.

Basically if you want to fix your issue, you need to replace a = () => { ... } with a() { ... }:

symbolCellLostFocus(symbolField : MatInput, $index: number) {
  console.log("base class symbol cell lost focus");
  //implementation...
}
symbolCellLostFocus(symbolField : MatInput, $index: number) {
  console.log("derived class symbol lost focus");
  super.symbolCellLostFocus(symbolField, $index);
}

Now for the explanation, if you write the following snippet:

class A {
    name = 'A'
    sayHello = () => {
        console.log('Hello from A')
    }
}

class B extends A {
    name = 'B'
    sayHello = () => {
        console.log('Hello from B')
        super.sayHello()
    }
}

It is transformed into this JavaScript snippet:

class A {
    constructor() {
        this.name = 'A';
        this.sayHello = () => {
            console.log('Hello from A');
        };
    }
}
class B extends A {
    constructor() {
        super(...arguments);
        this.name = 'B';
        this.sayHello = () => {
            console.log('Hello from B');
            super.sayHello();
        };
    }
}

As you can see, the methods are defined in the constructor, for each instance created by the constructors. This means that the method sayHello from A is not available for B, because sayHello is not available in the prototype of B (which is A), it is only available for each instance of A. This can be confusing, I highly recommend learning about prototype inheritance in JavaScript!

Classes, introduced in ES2015, are just syntactic sugar for prototype inheritance in JavaScript. The class, constructor, super, etc. keywords only abstract the syntax needed to write prototype chains. Essentially, it's the way to achieve composition and inheritance in JavaScript, a very powerful concept.

Anyway, when you write super.X() here, the JavaScript engine is trying to access the X method on the prototype, which doesn't exist. You end up with Object.getPrototypeOf(this).undefined(), and undefined is indeed not a function, so you get a TypeError: (intermediate value).sayHello is not a function runtime error.

Here's an example to illustrate what I said: TS playground.

Ipsambul answered 23/10, 2020 at 11:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.