如何在TypeScript中对联合类型进行类型收窄?(索引为变量场景)
TypeScript联合类型键检查与类型收窄解决方案
问题背景
你的actionsCollection是由多个结构相似但键集合不同的对象组成的联合类型,示例结构如下:
type ActionsCollection = { install: (...args) => Promise<any>; bid: (...args) => Promise<any>; } | { travel: (...args) => Promise<any>; bid: (...args) => Promise<any>; pass: (...args) => Promise<any>; };
当你通过in、hasOwnProperty等方式检查键是否存在后,TypeScript依然无法正确收窄类型,导致访问actionsCollection[actionType]或特定键时出现类型错误。
解决方法
1. 自定义类型守卫(推荐)
通过**类型谓词(Type Predicate)**手动告诉TypeScript,当前对象属于联合类型中包含指定键的成员:
首先拆分联合类型为单独的类型(便于后续类型收窄):
// 拆分联合类型成员为独立类型 type InstallBidActions = { install: (p: DefaultActionParams & { polynominoId: number; polynominoOrientation: number; polynominoPosition: number; }) => Promise<ProcessPlayerActionReturn>; bid: (p: DefaultActionParams & { /* 补充你的类型定义 */ }) => Promise<ProcessPlayerActionReturn>; }; type TravelBidPassActions = { travel: (p: DefaultActionParams & { /* 补充你的类型定义 */ }) => Promise<ProcessPlayerActionReturn>; bid: (p: DefaultActionParams & { /* 补充你的类型定义 */ }) => Promise<ProcessPlayerActionReturn>; pass: (p: DefaultActionParams & { /* 补充你的类型定义 */ }) => Promise<ProcessPlayerActionReturn>; }; type ActionsCollection = InstallBidActions | TravelBidPassActions;
然后定义通用类型守卫函数:
/** * 检查对象是否包含指定键,并让TypeScript完成类型收窄 */ function hasAction<T extends string>(obj: ActionsCollection, action: T): obj is Extract<ActionsCollection, Record<T, unknown>> { return action in obj; }
在业务代码中替换原有的多重检查:
if (!hasAction(actionsCollection, actionType)) { return socket!.emit('socketError', ERRORMESSAGES.UNKNOWNACTION); } // 此时TypeScript能正确识别actionsCollection包含actionType对应的键 const ttt = actionsCollection[actionType];
2. 针对特定键的分支收窄
如果需要单独处理某个特定键(比如install),直接用in操作符做分支判断即可,TypeScript会自动收窄类型:
if ('install' in actionsCollection) { // 这里actionsCollection会被自动收窄为InstallBidActions类型 const installAction = actionsCollection.install; // 可以安全调用installAction }
报错原因说明
TypeScript对联合类型的索引操作有严格的类型检查:当你用一个包含多个可能值的联合类型(比如"install" | "bid" | "travel")去索引联合类型时,TS无法确定当前actionsCollection的具体成员是否包含该键,因此会抛出类型错误。而自定义类型守卫通过Extract工具类型,从联合类型中精准提取出包含指定键的成员,让TS明确当前对象的类型范围。
另外,原代码中的Object.keys(actionsCollection).includes(actionType)无法用于类型收窄,因为Object.keys返回的是string[],TS无法将其与具体的键类型关联起来,建议移除这类无效检查。
内容的提问来源于stack exchange,提问作者Jay Dee
相关产品推荐
相关产品推荐

