代码之家  ›  专栏  ›  技术社区  ›  bugs

Typescript-泛型类型扩展自身

  •  9
  • bugs  · 技术社区  · 7 年前

    我最近遇到了这样的事情:

    interface Test<T extends Test<T>> {
      a: number;
      b: T;
    }
    
    function foo <T extends Test<T>>(el: T): T {
      ...
    }
    

    我不得不说,我有点困惑,到底这是什么,为什么需要这样的东西。我经历了 Generics section 但找不到任何类似的。

    该接口实现了什么,而使用下面这样的东西无法实现?

    interface Test<T>
    

    有人能解释一下吗?

    2 回复  |  直到 7 年前
        1
  •  12
  •   jcalz    7 年前

    没有实际的例子,我只能泛泛而谈。这种语法是像Java这样的语言所需要的,它没有 polymorphic this types ,我很快就会讲到。


    其思想是,您需要一个泛型类型来引用 相同类型 作为其包含类或接口。让我们看看你的 Test 接口:

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

    这描述了一种类似链表的结构,其中 b a的属性 Test<T> 还必须是 测试(<);T> 自从 T 扩展 测试(<);T> 。但除此之外,它必须是的(子类型) 相同类型 作为父对象。下面是两个实现的示例:

    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 是的实现 测验 ,但它们不能互换。这个 B a的属性 巧克力测试 是一个 巧克力测试 ,而 B a的属性 瓦尼拉试验 是一个 瓦尼拉试验 因此,出现以下错误,这是可取的:

    choc.b = vani; // error!
    

    现在你知道当你有 巧克力测试 整个名单上都是 巧克力测试 不必担心其他问题 测验 显示:

    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 属性作为 相同的 键入作为父级,只是为了实现 重新执行测试 。到目前为止,它看起来是一样的,但以下行为不同:

    relaxedChoc.b = relaxedVani; // no error
    

    这是允许的,因为 relaxedChoc.b 属于类型 重新执行测试 哪一个 relaxedVani 与兼容。鉴于 choc.b 属于类型 Test<ChocolateTest> 哪一个 vani 与兼容。


    一个类型将另一个类型约束为 与原始类型相同 是有用的。它非常有用,事实上,TypeScript有一个叫做 polymorphic 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!
    

    这与最初的行为几乎相同 Test<T extends Test<T>> 没有 possibly mind-bending circularity 所以,是的,我建议使用多态 相反,除非你有令人信服的理由用另一种方式来做。

    既然你说你遇到了这段代码,我想知道它是不是在引入多态之前的代码 或者是不知道的人,或者是我不知道有什么令人信服的原因。不确定。


    希望这对你有帮助。祝你好运

        2
  •  2
  •   Dhruv Parekh    5 年前
    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;
    }