【问题标题】:Using recursive type alias in generic results in error在泛型中使用递归类型别名会导致错误
【发布时间】:2020-02-18 14:53:22
【问题描述】:

当我尝试在泛型函数中使用递归类型别名时,TypeScript v3.7.5 会抛出 Type instantiation is excessively deep and possibly infinite.(2589)

type State = {
    head: {
        title: string;
        description: string;
        metadata: { type: string; value: string }[];
    };
    body: {
        title: string;
    };
};

cons...

Playground Link

为什么会这样?有没有办法在不提供最大深度值(迭代器)的情况下解决这个问题?

【问题讨论】:

    标签: typescript


    【解决方案1】:

    我们可以通过引入一些迭代器来使其有限。在下面的示例中,我将递归限制为 5 个级别:

    type I = 0 | 1 | 2 | 3 | 4 | 5;
    type Iterate<A extends I = 0> = 
        A extends 0 ? 1
        : A extends 1 ? 2
        : A extends 2 ? 3
        : A extends 3 ? 4
        : A extends 4 ? 5
        : A extends 5 ? 5
        : 5
    
    type Paths<Obj, X extends I = 0> = Iterate<X> extends 5 ? [] : Obj extends object
        ? {
              [Key in keyof Obj]: Prepend<Paths<Obj[Key], Iterate<X>>, Key>;
          }[keyof Obj]
        : [];
    

    核心部分是

    • Iterate&lt;X&gt; extends 5 ? [] 表示在第五层我们停止递归。
    • Paths&lt;Obj[Key], Iterate&lt;X&gt;&gt; 每次使用 Paths 都会增加 I

    几乎没有什么原因。 TS 的行为与 3.4 版本一样。一些相关的信息

    当我们遇到实例化深度限制时,我们通过解析为 any 来强制类型终止。 3.4 中唯一的变化是我们现在报告一个错误,让您知道我们正在提供截断(和不可预测)的结果。以前我们只是默默地让它过去。所以,我很确定你之前触发了限制器,只是你没有听说过。

    TS 对递归类型有 50 次迭代的隐式限制,但他们希望防止这种类型达到限制,因为这种类型会显着降低编译器的速度。这就是为什么现在开发人员需要限制类型以释放编译器时间。

    更多信息可以找到here

    【讨论】:

    • 感谢您的回答,但我很好奇为什么会发生此错误。我也知道迭代器解决方案。问题中提到了这一点。
    • 啊没看到。为什么 - 因为这样的类型可能是无限的,这意味着如果你在代码库中有这样的类型,你会得到很长的编译时间,这就是 TS 想要防止的
    • 添加更多信息为什么
    • 如果您不使用额外的类型库 (+1),这可能是更安全的选择
    【解决方案2】:

    编辑:

    请阅读@jcalz 在评论中提到的问题,以确保您了解下面的变通方法的含义。

    如果您实现自己的自定义递归类型,最好使用迭代计数器安全地限制递归深度,就像@Maciej Sikora 建议的那样(特别是如果您发布公共库)。


    解决方法

    对于递归类型,有时无法避免此错误。还好有workaround

    type Paths<Obj> = Obj extends object
        ? {
            [Key in keyof Obj]: Obj[Key] extends infer I ? Prepend<Paths<I>, Key> : never
            // add this         ^                                                       ^
        }[keyof Obj]
        : [];
    

    超过infer 声明将推迟类型评估,因此不会命中硬实例化限制计数器。因此,这将防止 Type instantiation is excessively deep 编译错误。请务必彻底测试类型,因为您已使用转义舱口禁用了编译器安全检查。

    或者,您可以自己优化Paths 类型或使用类似ts-toolbelt 的库(作者@pirix-gh 发现了上述技巧并在他的库中使用了这种机制)。

    更多链接

    【讨论】:

    猜你喜欢
    • 2020-10-18
    • 2022-12-14
    • 2012-11-09
    • 2021-04-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-14
    • 1970-01-01
    相关资源
    最近更新 更多