这里如何处理这个错误?我可以限制递归深度,或者只告诉TS它是好的吗?
export type StateUnion<T> = T extends { states: Record<infer K, any> }
? K | StateUnion<T['states'][K]>
: never;
这里如何处理这个错误?我可以限制递归深度,或者只告诉TS它是好的吗?
export type StateUnion<T> = T extends { states: Record<infer K, any> }
? K | StateUnion<T['states'][K]>
: never;
当你有一个像StateUnion
这样的recursive conditional type,并将它应用于一个本身是递归的类型,比如Config
,你很容易陷入编译器抱怨递归深度的情况.有时,您可以调整其中一个或两个定义来回避问题,但这些方法往往是反复试验的(至少在我的经验中是这样).
您可以try 的一种方法是为递归条件类型提供显式深度限制机制, Select 一个合理的默认最大深度,但允许用户更改它.对于你的StateUnion
人来说,它可能是这样的:
type StateUnion<T, D extends number = 8, A extends any[] = []> =
A['length'] extends D ? never : (
T extends { states: Record<infer K, any> }
? K | StateUnion<T['states'][K], D, [0, ...A]>
: never);
这里的预期用途是StateUnion<T, D>
,其中D
是对应于所需最大深度的数字literal type.如果您写入StateUnion<T>
而不指定D
,则最大深度为8
(但您可以更改该值).
不幸的是,TypeScrip(从5.1版开始)不知道如何在类型级别直接计算数学,所以我不能轻易地说出StateUnion<T['states'][K], D-1>
这样的东西.相反,我求助于使用tuple types和variadic tuple types来操作这些类型,其中tuple types具有已知的数字文字length
属性.因此,不是常规的"如果Depth为0,则退出,否则将Depth减go 1并递归",我必须说类似于"如果此数组的长度与最大深度相同,则Bal out,否则将一个元素附加到数组并递归".所以我在那里有累加器数组A
,我们正在判断A["length"]
,并将[0, ...A]
作为新A
传入.
让我们来测试一下:
interface Test {
states: { a: { states: { b: { states: { c: {
states: { d: { states: { e: Test } } }
} } } } } }
}
type SU2 = StateUnion<Test, 2>
//type SU2 = "a" | "b"
type SU4 = StateUnion<Test, 4>
//type SU4 = "a" | "b" | "c" | "d"
type SU8 = StateUnion<Test> // defaults to 8
// type SU8 = "a" | "b" | "c" | "d" | "e"
它按预期工作,如果我做了更改,那么您的原始代码StateUnion<Config>
(或StateUnion<T>
,其中T extends Config
)编译时不会出错.