联合类型+映射类型下Switch Case的TypeScript类型推断问题
TypeScript无法根据actionType推断payload类型的问题
在以下代码片段中,TypeScript编译器无法根据actionType推断参数(action.payload)的类型:
enum Actions{ ACTIONA, ACTIONB }; type ActionA = { type : Actions.ACTIONA, payload: { body: string; message: object; } } type ActionB = { type : Actions.ACTIONB, payload: { message: object; } } type ActionType = ActionA | ActionB; const actionHandlers: { [key in Actions.ACTIONA | Actions.ACTIONB]: (payload: Extract<ActionType, { type: key }>['payload']) => void } = { [Actions.ACTIONA]: ({body, message}) => console.log(body, message), [Actions.ACTIONB]: ({message}) => console.log( message), } const performAction = (action: ActionType): void => { switch(action.type){ case Actions.ACTIONA: case Actions.ACTIONB: return actionHandlers[action.type](action.payload); } }
错误信息:
Argument of type '{ body: string; message: object; } | { message: object; }' is not assignable to parameter of type '{ body: string; message: object; }'. Property 'body' is missing in type '{ message: object; }' but required in type '{ body: string; message: object; }'
一种临时解决方案是为每个case重复编写处理逻辑,但在实际代码场景中,case处理逻辑可能非常复杂,重复编写并非最优解:
case Actions.ACTIONA: return actionHandlers[action.type](action.payload); case Actions.ACTIONB: return actionHandlers[action.type](action.payload);
解决方案
问题核心是合并case分支后,TypeScript无法精准关联action.type与对应payload的类型约束。以下是几种更优的解决方式:
方式1:类型断言缩小范围
在调用handler时,通过类型断言明确当前action对应的payload类型:
const performAction = (action: ActionType): void => { switch(action.type){ case Actions.ACTIONA: case Actions.ACTIONB: return actionHandlers[action.type]( action.payload as Extract<ActionType, { type: typeof action.type }>['payload'] ); } }
方式2:重构Handler类型定义
重新定义映射类型,让每个action类型与payload的关联更清晰,配合never断言绕过编译器的推断限制(实际类型安全已由Handler类型保障):
type ActionHandlers = { [T in ActionType['type']]: (payload: Extract<ActionType, { type: T }>['payload']) => void } const actionHandlers: ActionHandlers = { [Actions.ACTIONA]: ({body, message}) => console.log(body, message), [Actions.ACTIONB]: ({message}) => console.log(message), } const performAction = (action: ActionType): void => { actionHandlers[action.type](action.payload as never); }
方式3:使用函数重载
为performAction添加重载声明,明确每个action类型对应的参数约束,让TypeScript自动完成类型推断:
function performAction(action: ActionA): void; function performAction(action: ActionB): void; function performAction(action: ActionType): void { actionHandlers[action.type](action.payload); }
内容的提问来源于stack exchange,提问作者Anmol Kansal
相关产品推荐
相关产品推荐

