为合并动作处理对象的Reducer编写正确的TypeScript类型
解决方案
要实现将拆分的动作处理函数合并后直接调用,核心是让TypeScript正确关联动作类型与对应处理函数的参数类型,以下是完整的类型定义和实现步骤:
1. 定义状态与动作类型
首先明确全局状态和所有动作的联合类型,确保每个动作包含唯一type和对应结构的payload:
type State = { count: number; name: string }; // 单个动作类型 type IncrementAction = { type: 'INCREMENT'; payload: { amount: number } }; type DecrementAction = { type: 'DECREMENT'; payload: { amount: number } }; type SetNameAction = { type: 'SET_NAME'; payload: { newName: string } }; // 所有动作的联合类型 type AllActions = IncrementAction | DecrementAction | SetNameAction;
2. 定义动作处理映射类型
创建通用映射类型,将每个动作的type映射为对应的处理函数,确保函数参数的payload类型与动作严格匹配:
type ActionHandlerMap<StateType, ActionType extends { type: string; payload: unknown }> = { [ActionKey in ActionType['type']]: ( state: StateType, payload: Extract<ActionType, { type: ActionKey }>['payload'] ) => StateType; };
3. 拆分定义动作处理函数
按照业务模块拆分处理函数,每个模块的处理函数都遵循上面定义的映射类型:
// 计数相关处理 const countHandlers: ActionHandlerMap<State, IncrementAction | DecrementAction> = { INCREMENT: (state, payload) => ({ ...state, count: state.count + payload.amount }), DECREMENT: (state, payload) => ({ ...state, count: state.count - payload.amount }), }; // 名称相关处理 const nameHandlers: ActionHandlerMap<State, SetNameAction> = { SET_NAME: (state, payload) => ({ ...state, name: payload.newName }), };
4. 合并处理函数并实现Reducer
合并处理函数时,通过类型断言让TypeScript识别合并后的完整映射关系;Reducer中直接通过action.type调用对应处理函数,同时增加默认分支处理未知动作:
const allHandlers = { ...countHandlers, ...nameHandlers, } as ActionHandlerMap<State, AllActions>; function reducer(state: State, action: AllActions): State { const handler = allHandlers[action.type]; return handler ? handler(state, action.payload) : state; }
关键说明
ActionHandlerMap通过Extract工具类型,从动作联合类型中精准提取对应type的payload类型,避免类型宽泛化。- 合并处理函数时的类型断言是必要的,因为TypeScript默认会将合并后的对象类型推断为交叉类型,丢失
type与payload的关联关系。 - 增加默认分支可以避免因未知动作导致的运行时错误,同时满足TypeScript的类型完整性检查。
内容的提问来源于stack exchange,提问作者Tom
相关产品推荐
相关产品推荐

