接口实现的Typescript通用推断

Sha*_*ean 7 typescript typescript-generics

我试图根据传入的参数的泛型来推断方法的返回类型。但是,该参数是来自泛型接口的实现,因此,我假设打字稿推断将根据参数的基数来确定类型。

示例代码:

interface ICommand<T> {}

class GetSomethingByIdCommand implements ICommand<string> {
  constructor(public readonly id: string) {}
}

class CommandBus implements ICommandBus {
  execute<T>(command: ICommand<T>): T {
    return null as any // ignore this, this will be called through an interface eitherway
  }
}

const bus = new CommandBus()
// badResult is {}
let badResult = bus.execute(new GetSomethingByIdCommand('1'))

// goodResult is string
let goodResult = bus.execute<string>(new GetSomethingByIdCommand('1'))
Run Code Online (Sandbox Code Playgroud)

我想做的是第一次execute调用,并让Typescript推断正确的返回值,string在这种情况下,此返回值是基于GetSomethingByIdCommand实现的。

我试过使用条件类型,但不确定这是否是解决方案或如何应用它。

jca*_*alz 9

您的问题是ICommand<T>结构上不依赖T(如@CRice的注释中所述)。

这是不推荐使用。(?链接到TypeScript FAQ条目,其中详细说明了一个与此案例几乎完全相同的案例,因此与我们可能在此处得到的官方用语一样近)

TypeScript的类型系统(主要)是结构性的,而不是名义上的:两种类型是相同的,当且仅当它们具有相同的形状(例如,具有相同的属性),并且与它们是否具有相同的名称无关。如果ICommand<T>不是结构性依赖T,没有它的性能有什么关系T,然后ICommand<string>同一类型ICommand<number>,这是同类型的ICommand<ICommand<boolean>>,这是相同的类型ICommand<{}>。是的,这些都是不同的名称,但是类型系统不是名义上的,因此没有多大用处。

在这种情况下,您不能依靠类型推断。当您调用时execute(),编译器会尝试推断Tin 的类型ICommand<T>,但是没有任何可推断的类型。因此,它最终默认为空类型{}

解决此问题的方法是ICommand<T>在某种程度上使结构依赖于T,并确保实现的任何类型ICommand<Something>都能正确执行此操作。给定示例代码,执行此操作的一种方法是:

interface ICommand<T> { 
  id: T;
}
Run Code Online (Sandbox Code Playgroud)

因此,ICommand<T>必须具有idtype 的属性T。幸运的是,GetSomethingByIdCommand实际上确实具有idtype 的属性string,如所要求implements ICommand<string>,因此可以正常编译。

而且,重要的是,您确实需要进行推断:

// goodResult is inferred as string even without manually specifying T
let goodResult = bus.execute(new GetSomethingByIdCommand('1'))
Run Code Online (Sandbox Code Playgroud)

好的,希望能有所帮助;祝好运!