类型谓词能否与泛型条件类型配合使用?
TypeScript类型守卫无法正确收窄条件类型参数的问题
我写了一段TypeScript代码,期望通过类型谓词的类型守卫把props的类型收窄为StrLike<T>,但实际得到的类型却是(T extends string ? ObjLike<T> : StrLike<T>) & StrLike<T>,达不到预期的收窄效果:
type ObjLike<T> = { kind: "object"; options: T[]; }; type StrLike<T> = { kind: "string"; options: T[]; }; function fn<T>(props: T extends string ? ObjLike<T> : StrLike<T>) { function isStrGuard(item: ObjLike<T> | StrLike<T>): item is StrLike<T> { return item.kind === "string"; } if (isStrGuard(props)) { /** * expected type: props: StrLike<T> * actual type: props: (T extends string ? ObjLike<T> : StrLike<T>) & StrLike<T> */ props; } }
问题原因
props的类型是条件类型,它的结果依赖于泛型T的具体类型。在泛型T未被具体化(还没传入具体类型参数)时,TypeScript没办法确定条件类型会走到哪个分支,所以在类型守卫后只能保留条件类型和StrLike<T>的交叉类型,无法直接简化为单纯的StrLike<T>。
解决方案
方案1:使用函数重载明确分支类型
通过给函数添加重载签名,把不同泛型分支下的参数类型明确出来,TypeScript就能正确识别类型守卫后的类型:
type ObjLike<T> = { kind: "object"; options: T[]; }; type StrLike<T> = { kind: "string"; options: T[]; }; // 当T是string类型时,参数为ObjLike<T> function fn<T extends string>(props: ObjLike<T>): void; // 当T不是string类型时,参数为StrLike<T> function fn<T>(props: StrLike<T>): void; // 函数实现 function fn<T>(props: ObjLike<T> | StrLike<T>) { function isStrGuard(item: ObjLike<T> | StrLike<T>): item is StrLike<T> { return item.kind === "string"; } if (isStrGuard(props)) { props; // 现在类型正确收窄为StrLike<T> } }
方案2:重构参数类型为联合类型
把条件类型拆成联合类型,结合never来排除不符合的分支,也能让类型守卫正常工作:
type ObjLike<T> = { kind: "object"; options: T[]; }; type StrLike<T> = { kind: "string"; options: T[]; }; function fn<T>(props: (T extends string ? ObjLike<T> : never) | StrLike<T>) { function isStrGuard(item: ObjLike<T> | StrLike<T>): item is StrLike<T> { return item.kind === "string"; } if (isStrGuard(props)) { props; // 类型为StrLike<T> } else { props; // 类型为ObjLike<T>(仅当T是string时) } }
内容的提问来源于stack exchange,提问作者Kyung Lee
相关产品推荐
相关产品推荐

