首页 > 解决方案 > 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实用程序类的某个用户创建了一个类YetAnotherFetcherDataFetcher_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被省略)

标签: typescript

解决方案


因为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);
  }
}

推荐阅读