Search code examples
javascripttypescriptclassjsdoc

Typescript erroring on property defined in subclass in javascript file


I'm using Typescript to check my Javascript files. When I extend from a class, Typescript no longer knows the types of the properties in the parent class. I'm curious if I'm missing something obvious, or if Typescript simply doesn't support checking this:

class Foo {

/**
 * @param {number} a
 */
  constructor(a) {

    this.a = a;

  }
}

class Bar extends Foo {

  constructor() {
    super(5);
  }

  move() {
     const foo = this.a + 5;
     this.a = foo;

  }

}

Error:

    'foo' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.
    'a' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.

I would have expected Typescript to understand that this.a has the type number, because it does understand this when the property is defined in the current class (instead of a parent class)


Solution

  • In the move method on Bar, there's a circular type inference: you set Bar.a to the value foo, which is computed using Bar.a without an explicit annotation — thus the error:

    ... implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.(7022)

    Here are three solutions:

    1. Prevent the unnecessary inference — don't use an intermediate variable:

      TS Playground

      move() {
        this.a = this.a + 5;
      }
      
    2. Explicitly annotate (cast) the type of foo:

      TS Playground

      move() {
        const foo = /** @type {number} */(this.a + 5);
        this.a = foo;
      }
      
    3. Get the value from this (Bar), but set the value to super (Foo) — and maintain discrete inference sites:

      TS Playground

      move() {
        const foo = this.a + 5;
        super.a = foo;
      }
      

      Accesses of the property a on instances of Bar will still resolve from the prototype property.