TypeScript:推断抽象方法实现的类型
我希望有一个基类,它有一个属性,其类型是从一个方法的返回值推断出来的,而该方法由子类实现。
所以,基类可以像这样:
abstract class MyBase<T> {
protected myProperty: T;
constructor() {
this.myProperty = this.getValueForMyProperty();
}
protected abstract getValueForMyProperty(): T;
}
以及一个后代:
class MyDescendant extends MyBase<MyDescendant1PropertyType> { // MyDescendant1PropertyType should be like ReturnType<(MyDescendant.getValueForMyProperty)>
protected getValueForMyProperty(): MyDescendant1PropertyType {
return {
prop1: "smg"
};
}
}
type MyDescendant1PropertyType = {
prop1
}
现在这可以工作,但在类型定义上有很多样板噪音。
我想描述这个规则:
MyBase.myProperty 的类型是 MyDescendant.getValueForMyProperty 类型。
我不需要 MyBase 中的类型信息,只需要 MyDescendant 中的。
所以我想要的是像这样的东西:
(这段代码无法编译)
abstract class MyBase<T /*infer from below, don't want to type it in descendants*/> {
protected myProperty: ReturnType<getValueForMyProperty>; // Don't know the right syntax for this
constructor() {
this.myProperty = this.getValueForMyProperty();
}
protected abstract getValueForMyProperty(): T;
}
class MyDescendant extends MyBase { // Don't want to add the generic part, MyBase<anything>
protected getValueForMyProperty() { // Don't want to manually add return type
return { // Return type should be inferred
prop1: "smg"
};
}
}
我可以在 TypeScript 中做这个吗?
答案#1
我有一个几乎符合你需求的解决方案。限制是为了让它工作,getValueForMyProperty 必须是 public。
这个设置的关键在于,我们不是用通用的 T 来描述返回类型,而是用 T 来描述对象本身。这样,我们就可以像这样创建特定实例:
class MyDescendant extends MyBase<MyDescendant> {
public getValueForMyProperty() {
return {
prop1: "smg"
};
}
}
为了做到这一点,我们说 MyBase 上的泛型 T 必须扩展这个接口:
interface CanGetProperty {
getValueForMyProperty(): unknown;
}
TypeScript 接口只描述对象的 public 属性,不能使用像 protected 或 private 这样的修饰符。这就是为什么这个设置要求 MyDescendent 上的 getValueForMyProperty 方法必须是 public 的。否则,它将无法满足所需的接口。
不知为何,当我用一个 public 方法实现 protected abstract 方法时,并没有出现错误?这似乎应该是一个错误,所以我也从 MyBase 中移除了 protected。
MyBase 的类型现在有点乱,但没关系,因为这里只是一个地方。
abstract class MyBase<T extends CanGetProperty> {
protected myProperty: ReturnType<T['getValueForMyProperty']>;
constructor() {
this.myProperty = this.getValueForMyProperty();
}
abstract getValueForMyProperty(): ReturnType<T['getValueForMyProperty']>;
}
我在这个 TypeScript Playground 中进行了测试,并且我在具有不同类型的子元素上获得了很好的返回类型推断。
