TypeScript:类型示例化太深,可能是无限的

2fjabf4q  于 2022-12-05  发布在  TypeScript
关注(0)|答案(1)|浏览(1064)

我正在尝试在TypeScript中实现一个通用的拆分类型,它能够使用多个分隔符拆分文字字符串。

type Spl<T extends string, D extends string> = T extends `${infer A}${D}${infer B}` ? 
    A extends '' ? [] :
    [A, ...Spl<B, D>] : (T extends '' ? [] : [T]);

type SplMany<T extends string, D extends string[]> = D extends [infer H extends string, ...infer Tail extends string[]]
    ? 
        T extends '' ? [] : SplManyInputsManyDelimieter<[...Spl<T, H>], Tail> 
    : [T];

type SplManyInputs<T extends string[], D extends string> = T extends [infer H extends string, ...infer Tail extends string[]]
    ? [...Spl<H, D>, ...SplManyInputs<Tail, D>] : [];

type SplManyInputsManyDelimieter<T extends string[], D extends string[]> = 
  D extends [infer H extends string, ...infer Tail extends string[]] ?
    SplManyInputsManyDelimieter<[...SplManyInputs<T, H>], Tail>
    : T;    

type t1 = Spl<'1 2 3 4 5 6                            7 8 9 10 11 %test% 12 13 14 15 16 17 18 19 20 21 22 23 24 25 1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 1 2 3 4 5 6                            7 8 9 10 11 %test% 12 13 14 15 16 17 18 19 20 21 22 23 24 25 1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 1 2 3 4 5 6                            7 8 9 10 11 %test% 12 13 14 15 16 17 18 19 20 21 22 23 24 25 1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 1 2 3 4 5 6                            7 8 9 10 11 %test% 12 13 14 15 16 17 18 19 20 21 22 23 24 25 1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 1 2 3 4 5 6                            7 8 9 10 11 %test% 12 13 14 15 16 17 18 19 20 21 22 23 24 25 1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4', ' '>;
type t1_ = Spl<'1 ', ' '>;
type t1__ = Spl<'1', ' '>;
type t1___ = Spl<'', ' '>;
type t3 = SplManyInputs<[' 1 2 3 ', ' 3 4 ', '5 6'], ' '>;
type t3_ = SplManyInputs<[], ' '>;
type t4 = SplManyInputsManyDelimieter<['1 2', '3 4', '5 6'], [' ']>;
type t4_ = SplManyInputsManyDelimieter<['1 2', '3 4', '5 6'], []>;
type t4__ = SplManyInputsManyDelimieter<[], [' ']>;

type test = SplMany<'0_1 0_2 0_3 0_4 0_5 0_6 0_7 0_8 0_9 1_0 1_1 1_2 1_3 1_4 1_5 1_6 1_7 1_8 1_9 2_0 2_1 2_2 2_3 2_4 2_5 2_6 2_7 2_8 2_9 3_0 3_1 3_2 3_3 3_4 3_5 3_6 3_7 3_8 3_9 4_0 4_1 4_2 4_3 4_4 4_5 4_6 4_7 4_8', [' ', '_']> // error!
// Type instantiation is excessively deep and possibly infinite.

查看TS游戏场链接
代码可以工作,但当输入足够长时,我得到错误Type instantiation is excessively deep and possibly infinite. ts(2589)。我猜这种拆分方式不是很有效,但我希望递归限制会更高一些。
有什么技巧可以优化代码以允许更长的输入吗?我还试图找到导致错误消息的TypeScript编译器部分,但没有找到。有人知道错误是在哪里发出的吗?

q3aa0525

q3aa05251#

当您撰写深度递归的条件型别,而您想要递归向下超过几十个层级时,可能会遇到递归限制,并出现“型别执行严修化深度过高且可能无限大”的错误消息。
在这种情况下,您通常可以重构,以便在microsoft/TypeScript#45711中实现的条件类型上使用尾部递归消除,并获得多达一千级的递归,这对于大多数用途来说通常已经足够了。
方法是确保任何递归使用的类型都是tail-recursive,其中递归类型的结果直接作为输出传递,不需要额外的操作。通常可以通过在类型函数中添加一个额外的 accumulator 类型参数来实现这一点。对于SplSplManyInputs,可能如下所示:

type Spl<T extends string, D extends string, ACC extends string[] = []> =
    T extends `${infer A}${D}${infer B}` ? A extends '' ? ACC :
    Spl<B, D, [...ACC, A]> : (T extends '' ? ACC : [...ACC, T]);

type SplManyInputs<T extends string[], D extends string, ACC extends string[] = []> =
    T extends [infer H extends string, ...infer Tail extends string[]]
    ? SplManyInputs<Tail, D, [...ACC, ...Spl<H, D>]> : ACC;

在这两种情况下,我都创建了一个ACC类型参数来保存最终的输出,它从空元组[]开始,然后逐渐填充中间结果。
您可以验证这是否给出了与您的版本相同的结果,但不会达到给定示例的递归限制。
Playground代码链接

相关问题