TypeScript:类型实例化过于深入,可能是无限的



我正在尝试在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 playground链接

代码工作,但当输入足够长时,我得到错误Type instantiation is excessively deep and possibly infinite. ts(2589)。我猜这样分割不是很有效,但我期望递归限制稍微高一些。

有什么技巧我可以做优化代码,以允许更长的输入?我还试图找到TypeScript编译器中负责错误消息的部分,但找不到。有人知道错误是在哪里发出的吗?

当您编写深度递归条件类型时,您希望递归下降超过几十层,您可能会遇到递归限制,并且"类型实例化"过深并且可能是无限的;错误消息。

在这种情况下,你通常可以重构为在microsoft/TypeScript#45711中实现的条件类型上使用尾部递归消除,并获得多达一千层的递归,这对于大多数用途来说通常已经足够好了。

方法是确保该类型的任何递归使用都是尾递归的,其中递归类型的结果直接作为输出传递,不需要额外的操作。通常,这可以通过向类型函数添加一个额外的累加器类型参数来实现。对于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链接到代码

相关内容

最新更新