typescript - TypeScript 类型泛型无法正确推断类型
问题描述
考虑以下代码:
interface Entity<T extends DataFetcherImp> {
name: string,
fetcher: () => T
}
type UUID = string;
declare abstract class DataFetcherImp {
public static fetch(id: UUID);
}
class TestFetcher implements DataFetcherImp {
public static async fetch(id: UUID): Promise<TestFetcher> {
return new TestFetcher(id);
}
constructor(private id: UUID) {
// perform some io
}
}
class OtherTestFetcher implements DataFetcherImp {
public static async fetch(id: UUID): Promise<OtherTestFetcher> {
return new OtherTestFetcher(id);
}
constructor(private id: UUID) {
// perform some other io
}
}
class DataFetcher_Broken {
public async getData<T extends DataFetcherImp>(entity: Entity<T>, id: UUID): Promise<T> {
return await entity.fetcher().fetch(id); // [2339] Property 'fetch' does not exist on type 'T'.
}
}
class DataFetcher_Working {
public async getData(entity: Entity<typeof TestFetcher>, id: UUID): Promise<TestFetcher>;
public async getData(entity: Entity<typeof OtherTestFetcher>, id: UUID): Promise<OtherTestFetcher>;
public async getData(entity: Entity<typeof TestFetcher | typeof OtherTestFetcher>, id: UUID): Promise<TestFetcher | OtherTestFetcher> {
return await entity.fetcher().fetch(id);
}
}
在我的理解中,这门课DataFetcher_Broken
应该是有效的。但是tsc
输出一个错误:[2339] Property 'fetch' does not exist on type 'T'.
编译器似乎希望在类型 T 上存在一个非静态方法 fetch。但是该方法必须是静态的,因为使用了一个库,该库仅在泛型抽象类上提供静态方法。
如您所见,该类DataFetcher_Working
完成了这项工作,但是还有另一个问题。如何扩展该类以与实现DataFetcherImp
抽象的其他类一起使用?还有我们无法控制的课程?让我们假设DataFetcher
实用程序类的某个用户创建了一个类YetAnotherFetcher
。DataFetcher_Working
如果不修改方法签名,则此类不能与 一起使用。所以这个方法不能包含在库中。
const df = new DataFetcher_Working();
df.getData({
name: 'yaf',
fetcher: () => YetAnotherFetcher // [2322] Type 'typeof YetAnotherFetcher' is not assignable to type 'typeof OtherTestFetcher'. Types of property 'fetch' are incompatible. Type '(id: string) => Promise<YetAnotherFetcher>' is not assignable to type '(id: string) => Promise<OtherTestFetcher>'. Type 'Promise<YetAnotherFetcher>' is not assignable to type 'Promise<OtherTestFetcher>'. Type 'YetAnotherFetcher' is not assignable to type 'OtherTestFetcher'. Types have separate declarations of a private property 'id'.
}, 'd6413b62-5bc7-4670-a28e-d14c822d1dd8');
因此,通用方法DataFetcher_Broken
是唯一的方法。
以下修改已尝试但无法正常工作:
1.
class DataFetcher_Broken {
public async getData<T extends DataFetcherImp>(entity: Entity<T>, id: UUID): Promise<T> {
return await (entity.fetcher() as any as typeof DataFetcherImp).fetch(id);
}
}
这种变化允许
const df = new DataFetcher_Broken();
df.getData({ name: 'completeDesaster', fetcher: () => Object }, 'd6413b62-5bc7-4670-a28e-d14c822d1dd8');
但Object
没有静态方法fetch
。
2.
class DataFetcher_Broken {
public async getData<T extends DataFetcherImp>(entity: Entity<T>, id: UUID): Promise<T> {
return await (entity.fetcher() as DataFetcherImp).fetch(id); // [2576] Property 'fetch' is a static member of type 'DataFetcherImp'
}
}
3.
interface Entity<T extends DataFetcherImp> {
name: string,
fetcher: () => typeof DataFetcherImp
}
这种变化有点道理,但是T
实体上的类型不再对fetcher
属性强制执行,它会产生另一个错误:
const df = new DataFetcher_Broken();
df.getData({ name: 'willNotCompile', fetcher: () => TestFetcher }, 'd6413b62-5bc7-4670-a28e-d14c822d1dd8'); // [2322] Type 'typeof TestFetcher' is not assignable to type 'typeof DataFetcherImp'.
有什么建议,如何让它工作?
TS Playground:最小的例子。(静态函数被复制到原型的部分TestFetcher
被省略)
解决方案
因为fetch
是静态的,它实际上不会存在于 的实例上DataFetcherImp
,而只会存在于typeof DataFetcherImp
.
以下是消除错误 ( T extends typeof DataFetcherImp
) 的更改:
class DataFetcher_Broken {
public async getData<T extends typeof DataFetcherImp>(entity: Entity<T>, id: UUID): Promise<T> {
return await entity.fetcher().fetch(id);
}
}
推荐阅读
- android - 将 gradle 版本更新到 5.1.1,但 kapt 不起作用
- c - 如何将 .h 文件的内容插入字符串以进行运行时编译?
- amazon-cognito - 服务器端:如何刷新过期的令牌?
- assembly - 汇编程序不打印读取字符串
- javascript - 检查未定义的var时如何避免异常?
- mongodb - 如何更新 MongoDB 中嵌套数组元素的字段
- jenkins - Jenkins Multibranch Pipeline - 以编程方式设置构建配置
- ocr - OCR jpeg 文件转文本
- jquery - 检查 $.get 响应并将用户重定向到 URL,但页面不断重新加载
- ckfinder - 是否可以在一个 CKFinder 视图中组合多个 baseDirs?