来自接口实现的 Typescript 通用推理 [英] Typescript generic inference from interface implementation

查看:27
本文介绍了来自接口实现的 Typescript 通用推理的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图从传入的参数的泛型推断方法的返回类型.但是,参数是泛型接口的实现,所以我假设打字稿推断会从参数的类型中确定类型基础.

I'm trying to infer the return type of a method from the generic of an argument passed in. However, the argument is an implementation from a generic interface, so I would assume typescript inferencing would have determined the type from the argument's base.

示例代码:

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'))

我想做的是第一个 execute 调用并让打字稿推断出正确的返回值,在这种情况下是 string 基于什么 GetSomethingByIdCommand 是从实现的.

What I'd like to do is the first execute call and have typescript infer the correct return value, which is string in this case based on what GetSomethingByIdCommand was implemented from.

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

I've tried playing around with conditional types but not sure if this is a solution or how to apply it.

推荐答案

您的问题是 ICommand 在结构上不依赖于 T(如@CRice 的评论).

Your problem is that ICommand<T> is not structurally dependent on T (as mentioned in @CRice's comment).

这是 不推荐.(⬅ 链接到 TypeScript 常见问题条目,其中详细说明了一个与此几乎完全相同的案例,因此这与我们在这里可能会得到的官方词一样接近)

This is not recommended. (⬅ link to a TypeScript FAQ entry detailing a case that is almost exactly the same as this one, so that's as close to an official word as we're likely to get here)

TypeScript 的类型系统(主要)是结构性的,而不是名义上的:当且仅当它们具有相同的形状(例如,具有相同的属性)并且它们是否具有相同的名称时,两种类型才是相同的.如果 ICommand结构上不依赖于 T,并且它的任何属性都与 T 无关code>,则ICommandICommand相同类型ICommand<是相同类型ICommand>,与 ICommand<{}> 的类型相同.是的,这些都是不同的名称,但是类型系统不是名义上的,所以这并不重要.

TypeScript's type system is (mostly) structural, not nominal: two types are the same if and only if they are the same shape (e.g., have the same properties) and it has nothing to do with whether they have the same name. If ICommand<T> isn't structurally dependent on T, and none of its properties have anything to do with T, then ICommand<string> is the same type as ICommand<number>, which is the same type as ICommand<ICommand<boolean>>, which is the same type as ICommand<{}>. Yes, those are all different names, but the type system isn't nominal, so that doesn't count for much.

在这种情况下,您不能依靠类型推断来工作.当您调用 execute() 时,编译器会尝试为 ICommand 中的 T 推断类型,但它无法从中推断出任何内容.所以它最终默认为空类型 {}.

You can't rely on type inference to work in such cases. When you call execute() the compiler tries to infer a type for T in ICommand<T>, but there's nothing from which it can infer. So it ends up defaulting to the empty type {}.

对此的解决方法是使 ICommand 在结构上以某种方式依赖于 T,并确保任何实现 ICommand 的类型; 这样做是正确的.根据您的示例代码执行此操作的一种方法是:

The fix for this is to make ICommand<T> structurally dependent in some way on T, and to make sure any type that implements ICommand<Something> does so correctly. One way to do this given your example code is this:

interface ICommand<T> { 
  id: T;
}

因此 ICommand 必须具有 T 类型的 id 属性.幸运的是,GetSomethingByIdCommand 实际上确实具有 string 类型的 id 属性,正如 implements ICommand 所要求的,这样编译就可以了.

So an ICommand<T> must have an id property of type T. Luckily the GetSomethingByIdCommand actually does have an id property of type string, as required by implements ICommand<string>, so that compiles fine.

而且,重要的是,您想要的推理确实发生了:

And, importantly, the inference you want does indeed happen:

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

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

Okay, hope that helps; good luck!

这篇关于来自接口实现的 Typescript 通用推理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆