打字稿-扩展自身的通用类型 [英] Typescript - Generic type extending itself

查看:59
本文介绍了打字稿-扩展自身的通用类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近遇到了一个看起来像这样的东西:

I recently came across something that looked like this:

interface Test<T extends Test<T>> {
  a: number;
  b: T;
}

function foo <T extends Test<T>>(el: T): T {
  ...
}

我不得不说我对这到底是什么以及为什么需要类似的东西感到困惑.我浏览过Typescript手册的泛型部分,但找不到任何类似的东西.

I have to say I am a bit perplexed as to what exactly this is, and why something like this would be required. I've been through the Generics section of the Typescript handbook but couldn't find anything similar.

该界面所能实现的功能无法通过以下方式完成?

What does that interface achieve that can't be done with something like the following?

interface Test<T>

任何人都可以对此有所了解吗?

Can anyone shed some light on this?

推荐答案

没有实际的示例,我只能概括地说.像Java这样没有多态的类型,我将在稍后介绍.

Without the actual example, I can only speak in generalities. That sort of syntax is what you need in a language like Java that doesn't have polymorphic this types, which I'll get to shortly.

这个想法是,您想要一个泛型类型,该泛型类型将相同类型的其他对象引用为其包含的类或接口.让我们看看您的 Test 界面:

The idea is that you want a generic type that refers to other objects of the same type as its containing class or interface. Let's look at your Test interface:

interface Test<T extends Test<T>> {
  a: number;
  b: T;
}

这描述了类似链表的结构,其中 Test< T> b 属性也必须是 Test< T> ,因为 T 扩展了 Test< T> .但此外,它必须与父对象相同类型(的子类型)相同.这是两个实现的示例:

This describes a linked-list-like structure where the b property of a Test<T> must also be a Test<T>, since T extends Test<T>. But additionally, it must be (a subtype of) the same type as the parent object. Here's an example of two implementations:

interface ChocolateTest extends Test<ChocolateTest> {
  flavor: "chocolate";
}
const choc = {a: 0, b: {a: 1, flavor: "chocolate"}, flavor: "chocolate"} as ChocolateTest;
choc.b.b = choc;

interface VanillaTest extends Test<VanillaTest> {
  flavor: "vanilla";
}
const vani = {a: 0, b: {a: 1, flavor: "vanilla"}, flavor: "vanilla"} as VanillaTest;
vani.b.b = vani;

ChocolateTest VanillaTest 都是 Test 的实现,但它们不可互换. ChocolateTest b 属性是 ChocolateTest ,而 VanillaTest 的 b 属性code>是 VanillaTest .因此会发生以下错误,这是所希望的:

Both ChocolateTest and VanillaTest are implementations of Test, but they are not interchangable. The b property of a ChocolateTest is a ChocolateTest, while the b property of a VanillaTest is a VanillaTest. So the following error occurs, which is desirable:

choc.b = vani; // error!

现在,您知道拥有 ChocolateTest 时,整个列表充满了其他 ChocolateTest 实例,而无需担心出现其他 Test :

Now you know when you have a ChocolateTest that the entire list is full of other ChocolateTest instances without worrying about some other Test showing up:

choc.b.b.b.b.b.b.b.b.b // <-- still a ChocolateTest


将此行为与以下界面进行比较:


Compare this behavior to the following interface:

interface RelaxedTest {
  a: number;
  b: RelaxedTest;
}

interface RelaxedChocolateTest extends RelaxedTest {
  flavor: "chocolate";
}
const relaxedChoc: RelaxedChocolateTest = choc;

interface RelaxedVanillaTest extends RelaxedTest {
  flavor: "vanilla";
}
const relaxedVani: RelaxedVanillaTest = vani;

您可以看到 RelaxedTest 并没有将 b 属性限制为与父级相同的 类型,只是实现了 RelaxedTest .到目前为止,它看起来相同,但是以下行为不同:

You can see that RelaxedTest doesn't constrain the b property to be the same type as the parent, just to some implementation of RelaxedTest. So far, it looks the same, but the following behavior is different:

relaxedChoc.b = relaxedVani; // no error

这是允许的,因为 relaxedChoc.b 的类型为 RelaxedTest ,与 relaxedVani 兼容.而 choc.b 的类型为 Test< ChocolateTest> ,而 vani 不兼容.

This is allowed because relaxedChoc.b is of type RelaxedTest, which relaxedVani is compatible with. Whereas choc.b is of type Test<ChocolateTest>, which vani is not compatible with.

一种类型将另一种类型约束为与原始类型相同的能力是有用的.实际上,它非常有用,TypeScript具有称为 polymorphic的东西 this 仅用于此目的.您可以使用 this 作为类型来表示与包含的类/接口相同的类型",并删除上面的通用内容:

That ability of a type to constrain another type to be the same as the original type is useful. It's so useful, in fact, that TypeScript has something called polymorphic this for just this purpose. You can use this as a type to mean "the same type as the containing class/interface", and do away with the generic stuff above:

interface BetterTest {
  a: number;
  b: this; // <-- same as the implementing subtype
}

interface BetterChocolateTest extends BetterTest {
  flavor: "chocolate";
}
const betterChoc: BetterChocolateTest = choc;

interface BetterVanillaTest extends BetterTest {
  flavor: "vanilla";
}
const betterVani: BetterVanillaTest = vani;

betterChoc.b = betterVani; // error!

行为与原始 Test< T扩展Test< T>> 几乎相同,而无需可能会弯腰.所以,是的,除非您有其他令人信服的理由,否则我建议您改用多态的 this .

This acts nearly the same as the original Test<T extends Test<T>> without the possibly mind-bending circularity. So, yeah, I'd recommend using polymorphic this instead, unless you have some compelling reason to do it the other way.

因为您说过您遇到了这段代码,所以我想知道它是来自引入多态的 this 之前的某个代码,还是某个不认识它的人编写的代码,或者是否存在某些代码?我不知道的令人信服的理由.不确定.

Since you said you came across this code, I wonder if it was some code from before the introduction of polymorphic this, or by someone who didn't know about it, or if there is some compelling reason I don't know about. Not sure.

希望对您有所帮助.祝你好运!

Hope that makes sense and helps you. Good luck!

这篇关于打字稿-扩展自身的通用类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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