美化联合类型时出现类型实例化过深或无限递归错误
TypeScript递归联合类型加Prettify后赋值报错的解决办法
我想让API使用者看到ACTION_1 | ACTION_2 | etc这种直观的联合类型输出,就写了这俩工具类型:
export type Prettify<T> = T extends infer R ? { [K in keyof R]: R[K] } : never; export type ActionUnion<T> = Prettify<{ [K in keyof T]: K extends `actions` ? keyof T[K] | ActionUnion<T[K]> : T[K] extends Record<string, unknown> ? ActionUnion<T[K]> : never; }[keyof T]>;
但把ActionUnion<T>用作machine函数里transition方法的参数类型时,赋值action给actionStr就报错了,去掉Prettify包着就没事:
type Config = { actions?: Record<string, string>; states?: Record<string, Config>; } export function machine<T extends Config>(config: T) { return { transition, }; function transition(action: ActionUnion<T>) { const actionStr = action; // <-- 报错:ActionUnion<T> 不能赋值给 string } }
注意:这个问题的递归逻辑和之前StackOverflow上的类似问题不一样,那些方案不管用。
问题原因
原来的Prettify会把所有类型都转成对象结构展开,哪怕是字符串联合类型也会被当成对象处理,导致TypeScript识别不出它其实是string的子集。递归生成的字符串字面量联合经过这层包装后,类型推断就乱了,自然没法赋值给string变量。
解决方案
改一下Prettify,只对对象类型做展开美化,非对象类型直接原封不动返回:
// 只处理对象类型,非对象直接返回原类型 export type Prettify<T> = T extends object ? T extends infer R ? { [K in keyof R]: R[K] } : never : T; export type ActionUnion<T> = Prettify<{ [K in keyof T]: K extends `actions` ? keyof T[K] | ActionUnion<T[K]> : T[K] extends Record<string, unknown> ? ActionUnion<T[K]> : never; }[keyof T]>;
这样一来,当ActionUnion<T>算出的是字符串联合时,Prettify直接把这个联合返回,TypeScript能正确识别它属于string类型,赋值就不会报错了,同时对象类型的美化效果也保留了。
内容的提问来源于stack exchange,提问作者Andy Jessop
相关产品推荐
相关产品推荐

