I would like to see if the return type of a function in an abstract class can be overridden by a child class:
Parent.ts
abstract class Parent<T> {
abstract data: T | T[];
// Can this return as T or T[] (not T | T[]) depending on implementation of
// child?
getData() {
return this.data;
}
}
Child.ts
class Child extends Parent<Person> {
data: Person[] = [];
}
index.ts
const child = new Child();
child.getData() // Can this be returned as Person[] automatically without typecast?
I know I can typecast in index.ts, but I was wondering if there is a way for TypeScript to automatically infer the type of data.
CodePudding user response:
As @jcalz pointed out in the comments. The answer is to use polymorphic this types. Thanks for your help!
CodePudding user response:
When you want a class to dynamically refer to the type of the "current" class instance, which could be a more specific subclass, you can use the polymorphic this type (that is, you use this as a type).
In fact, if you inspect the type of this inside getData()'s implementation, you will see that the compiler infers it to be of type this:
// this: this
But the compiler will often widen this to the class type, especially when you access a property; so while the type of this is seen as type this, the type of this.data is seen as type Parent<T>['data'], which is T | T[]. Instead, you want this['data'], meaning "the type of the data property on whatever this is".
If you want to prevent that widening of this['data'] to Parent<T>['data'], you can explicitly annotate a new variable of this type:
getData() {
const data: this['data'] = this.data;
return data;
}
Or you can annotate the return type of the method:
getData(): this['data'] {
return this.data;
}
Or you can assert that the return value is of that type:
getData() {
return this.data as this['data'];
}
Once you do this, then callers of getData() in subclasses will have the more specific type information you care about:
interface Person { name: string }
class Child extends Parent<Person> {
data: Person[] = [];
}
const child = new Child();
child.getData().map(person => person.name); // okay
