为什么稿不允许圆引用的泛型?

0

的问题

这里的例子,类直接引用本身在其定义中,但是当抽象通过一般的完全失败。

type a = { val: a }; // <-- doesn't care about circular references!

type record<T> = { val: T };

type b = record<b>; // <-- doesn't work!

type func<T> = (arg: T) => void;

type c = func<c>; // <-- doesn't work!

type d = (arg: d) => void; // <-- works!?
types typescript
2021-11-23 20:48:45
2

最好的答案

3

看到 microsoft/稿#41164 为一个典型的回答这个问题。

稿 允许圆引用 通用的 接口通用 课程,由于接口和类实例有静态已知财产/构件/方法 的关键s因此,任何圆发生在"安全"的地方,如财产 价值或方法的参数或者返回的类型。

interface Interface<T> { val: T }
type X = Interface<X> // okay

class Class<T> { method(arg: T): void { } }
type Y = Class<Y> // okay

但是,对通用 类型的别名 ,没有这样的保证。 类别名可能有任何结构的任何匿名的类型可以拥有,因此,潜在的循环是没有约束递归的树类物体:

type Safe<T> = { val: T };
type Unsafe<T> = T | { val: string };

当编译器实例一般类型,它 将推迟 它的评价;它不会立刻尝试完全计算的所得类型。 所有它认为是形式:

type WouldBeSafe = Safe<WouldBeSafe>; 
type WouldBeUnsafe = Unsafe<WouldBeUnsafe>; 

这些看起来相同的编译器... type X = SomeGenericTypeAlias<X>. 它不能"看见", WouldBeSafe 会好起来的:

//type WouldBeSafe = { val: WouldBeSafe }; // would be okay

同时 WouldBeUnsafe 将会是一个问题:

//type WouldBeUnsafe = WouldBeUnsafe | { val: string }; // would be error

因为它不能看到差别,并因为至少某些惯例将是非法的圆形的,它只是禁止所有的人。


那么,你能做些什么? 这是一个这些情况下,我建议使用 interface 而不是的 type 当你可以。 你可以重写你的 record 类型(改变它 MyRecord 命名《公约》的原因)为 interface 一切都会的工作:

interface MyRecord<T> { val: T };
type B = MyRecord<B>; // okay

你甚至可以重写你的 func 类型(改变它 Func 命名《公约》的原因再次)作为一个 interface 通过改变的 功能的类型表达 的语法成一个 呼吁的签名语法:

interface Func<T> { (arg: T): void }
type C = Func<C>; // okay

当然在有些情况下你不能这么直接,例如内置 Record 实用新型:

type Darn = Record<string, Darn>; // error

你不能改写 映类型 Record 作为一个 interface. 确实,这将是不安全的尝试使键圆的,就像 type NoGood = Record<NoGood, string>. 如果你只想要做的 Record<string, T> 对于一般 T可以 改写 作为一个 interface:

interface Dictionary<T> extends Record<string, T> { };
type Works = Dictionary<Works>;

因此,有相当经常的方式使用 interface 而不是的 type 为让你表达"安全"递归类型。

游乐场链接,以代码

2021-11-23 21:31:48

谢谢! 这是一个很酷的和有用的!
radiish
1

让我们仔细分析这些方案中的一个接一个。

方案1

type a = { val: a }; // <-- doesn't care about circular references!

这很有趣这是允许的。 我不看你如何能够构建一个实例,将满足这种类型:

const A: a = {
  val: {
    val: {
      // It will always error out at the most inner node.
    }
  }
}

方案2

type record<T> = { val: T };

这不是一个圆形的基准和可以满足这样的:

const B: record<string> = {
  val: "test"
}

方案3

type b = record<b>; // <-- doesn't work!

它对我来说很有意义,这不起作用。 就像在方案1中,就没有办法建造的一个实例,满足了这种约束。

方案4

type func<T> = (arg: T) => void;

这不是一个圆形的基准和可以满足这样的:

const C: func<string> = (arg: string) => {}

方案5

type c = func<c>; // <-- doesn't work!

它对我来说很有意义,这不起作用。 就像在方案1中,就没有办法建造的一个实例,满足了这种约束。

方案6

type d = (arg: d) => void; // <-- works!?

其实我可以写一个功能,以满足这种约束,但是我不知道什么让我:

const D: d = (arg) => {}
D(D)
2021-11-23 21:34:19

其他语言

此页面有其他语言版本

Русский
..................................................................................................................
Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................