这里的示例中,类型在其定义中直接引用自身,但是当通过泛型抽象时,它完全失败。
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!?
Run Code Online (Sandbox Code Playgroud)
有关此问题的规范答案,请参阅microsoft/TypeScript#41164 。
TypeScript确实允许泛型 接口和泛型类中的循环引用,因为接口和类实例具有静态已知的属性/成员/方法键,因此任何循环都发生在“安全”的地方,例如属性值或方法参数或返回类型。
interface Interface<T> { val: T }
type X = Interface<X> // okay
class Class<T> { method(arg: T): void { } }
type Y = Class<Y> // okay
Run Code Online (Sandbox Code Playgroud)
但对于泛型类型别名则没有这样的保证。类型别名可以具有任何匿名类型可以具有的任何结构,因此潜在的循环不限于递归树状对象:
type Safe<T> = { val: T };
type Unsafe<T> = T | { val: string };
Run Code Online (Sandbox Code Playgroud)
当编译器实例化泛型类型时,它会推迟其计算;它不会立即尝试完全计算结果类型。它看到的只是形式:
type WouldBeSafe = Safe<WouldBeSafe>;
type WouldBeUnsafe = Unsafe<WouldBeUnsafe>;
Run Code Online (Sandbox Code Playgroud)
对于编译器来说,这两个看起来都是一样的type X = SomeGenericTypeAlias<X>...... 它无法“看到”那WouldBeSafe没关系:
//type WouldBeSafe = { val: WouldBeSafe }; // would be okay
Run Code Online (Sandbox Code Playgroud)
而这WouldBeUnsafe会是一个问题:
//type WouldBeUnsafe = WouldBeUnsafe | { val: string }; // would be error
Run Code Online (Sandbox Code Playgroud)
由于它看不到差异,并且因为至少某些用法会被非法循环,所以它只是禁止所有这些用法。
所以,你可以做什么?interface这是我建议使用而不是当你可以时使用的情况之一type。您可以将您的record类型重写MyRecord为 an interface,一切都会正常工作:
interface MyRecord<T> { val: T };
type B = MyRecord<B>; // okay
Run Code Online (Sandbox Code Playgroud)
您甚至可以通过将函数类型表达式语法更改为调用签名语法func来重写您的类型(Func再次出于命名约定原因将其更改为 an) :interface
interface Func<T> { (arg: T): void }
type C = Func<C>; // okay
Run Code Online (Sandbox Code Playgroud)
当然,有些情况下你不能直接这样做,例如内置Record实用程序类型:
type Darn = Record<string, Darn>; // error
Run Code Online (Sandbox Code Playgroud)
并且您无法将映射类型 Record重写为interface. 事实上,尝试将密钥做成圆形是不安全的,例如type NoGood = Record<NoGood, string>。Record<string, T>如果您只想为 generic做T,您可以将其重写为interface:
interface Dictionary<T> extends Record<string, T> { };
type Works = Dictionary<Works>;
Run Code Online (Sandbox Code Playgroud)
因此,通常有一种方法可以使用 aninterface而不是type来表达“安全”递归类型。
| 归档时间: |
|
| 查看次数: |
1587 次 |
| 最近记录: |