【问题标题】:Typescript - Generic type extending itselfTypescript - 扩展自身的通用类型
【发布时间】:2018-05-10 15:31:06
【问题描述】:

我最近遇到了类似这样的东西:

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

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

我不得不说我有点困惑这到底是什么,以及为什么需要这样的东西。我已经浏览了 Typescript 手册的Generics section,但找不到类似的东西。

该接口实现了哪些无法通过以下方式实现的功能?

interface Test<T>

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

【问题讨论】:

    标签: typescript generics


    【解决方案1】:

    没有实际的例子,我只能笼统地说。这种语法是你在像 Java 这样没有polymorphic this types 的语言中所需要的,我很快就会讲到。


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

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

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

    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;
    

    ChocolateTestVanillaTest 都是 Test 的实现,但它们不能互换。 ChocolateTestb 属性是 ChocolateTest,而 VanillaTestb 属性是 VanillaTest。于是出现如下错误,这是可取的:

    choc.b = vani; // error!
    

    现在您知道,当您拥有 ChocolateTest 时,整个列表中都充满了其他 ChocolateTest 实例,而不必担心会出现其他 Test

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

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

    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 的某些实现。到目前为止,它看起来相同,但以下行为不同:

    relaxedChoc.b = relaxedVani; // no error
    

    这是允许的,因为relaxedChoc.bRelaxedTest 类型,relaxedVani 与之兼容。而choc.bTest&lt;ChocolateTest&gt; 类型,vani 与它兼容。


    一种类型将另一种类型限制为与原始类型相同的能力很有用。事实上,它非常有用,TypeScript 有一个叫做 polymorphic this 的东西就是为了这个目的。您可以使用this 作为类型来表示“与包含类/接口相同的类型”,并取消上面的通用内容:

    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!
    

    这与没有possibly mind-bending circularity 的原始Test&lt;T extends Test&lt;T&gt;&gt; 几乎相同。所以,是的,我建议改用多态this,除非你有一些令人信服的理由以另一种方式这样做。

    既然你说你遇到了这段代码,我想知道它是在引入多态this 之前的一些代码,还是由不知道它的人编写的,或者是否有一些令人信服的理由我不知道'不知道。不确定。


    希望这是有道理的,可以帮助你。祝你好运!

    【讨论】:

    • 多么好的答案!需要一些时间来完成它
    【解决方案2】:
    public static foo<TType extends number | string, T extends Tree<TType>>(data: T[]): T[] {
        console.log(data[0].key);
        return
    }
    
    
    export interface Tree<T> {
        label?: string;
        data?: any;
        parent?: Tree<T>;
        parentId?: T;
        key?: T;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-15
      • 2019-07-20
      • 1970-01-01
      • 2020-09-12
      • 2018-05-08
      • 1970-01-01
      相关资源
      最近更新 更多