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)
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:
Prevent the unnecessary inference — don't use an intermediate variable:
move() {
this.a = this.a + 5;
}
Explicitly annotate (cast) the type of foo
:
move() {
const foo = /** @type {number} */(this.a + 5);
this.a = foo;
}
Get the value from this
(Bar
), but set the value to super
(Foo
) — and maintain discrete inference sites:
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.