将Redux reducer分支逻辑抽为函数时遇TypeScript类型错误
解决TypeScript中Redux reducer分支函数的类型收窄问题
这是个很常见的TypeScript类型收窄场景,咱们来一步步解决这个报错问题~
核心原因
你遇到的错误本质是:当把action传入独立函数doSomething时,TypeScript只知道它是action联合类型,但无法确定它具体是"do-something"类型的action,所以不允许访问只有该分支才有的index属性。而在reducer的switch case里,TypeScript会根据action.type自动完成类型收窄,所以原来的写法没问题。
解决方案1:拆分action类型并指定精确参数类型
最优雅的方式是先把每个action的类型单独定义,再组成联合类型,这样给独立函数指定精确的action类型:
// 先定义单个action的具体类型 type DoSomethingAction = { type: "do-something"; index: number; }; type DoOtherThingAction = { type: "do-other-thing"; name: string; }; // 组合成联合类型 type Action = DoSomethingAction | DoOtherThingAction; // 假设你的state类型和初始状态是这样的(根据实际情况调整) type State = { /* 你的state结构 */ }; const initState: State = { /* 初始值 */ }; // 给doSomething的action参数指定精确的DoSomethingAction类型 function doSomething(state: State, action: DoSomethingAction): State { if (action.index === 0) return state; // 这里可以放心写更多逻辑,TypeScript已经确认action有index属性 return state; // 别忘了返回state,否则会报返回值类型错误 } function reducer(state: State = initState, action: Action): State { switch (action.type) { case "do-something": // 这里TypeScript会自动把action收窄为DoSomethingAction,所以调用doSomething是安全的 return doSomething(state, action); case "do-other-thing": // 同理,你也可以把这个分支的逻辑移到独立函数,指定DoOtherThingAction类型 // return doOtherThing(state, action); return state; default: return state; } }
解决方案2:在函数内部手动做类型守卫
如果不想拆分原有的action类型,也可以在doSomething内部通过类型守卫手动收窄action类型:
// 保持你原来的action联合类型定义 type Action = | { type: "do-something"; index: number; } | { type: "do-other-thing"; name: string; }; type State = { /* 你的state结构 */ }; const initState: State = { /* 初始值 */ }; function doSomething(state: State, action: Action): State { // 用类型守卫确认当前action是"do-something"类型 if (action.type !== "do-something") { // 这里可以抛出错误或者返回原state,确保逻辑安全 throw new Error("doSomething函数只能处理do-something类型的action"); } // 到这里TypeScript已经知道action有index属性了 if (action.index === 0) return state; // 更多逻辑 return state; } function reducer(state: State = initState, action: Action): State { switch (action.type) { case "do-something": return doSomething(state, action); case "do-other-thing": return state; default: return state; } }
额外建议:使用Redux Toolkit简化类型处理
如果你的项目可以引入Redux Toolkit(RTK),它会自动帮你处理action的类型收窄,不需要手动写这些类型定义,比如用createSlice的方式:
import { createSlice } from '@reduxjs/toolkit'; type State = { /* 你的state结构 */ }; const initState: State = { /* 初始值 */ }; const slice = createSlice({ name: 'yourSlice', initialState: initState, reducers: { doSomething: (state, action: { payload: { index: number } }) => { if (action.payload.index === 0) return; // 更多逻辑,RTK会自动处理immutable更新 }, doOtherThing: (state, action: { payload: { name: string } }) => { // 更多逻辑 } } }); export const { doSomething, doOtherThing } = slice.actions; export default slice.reducer;
这种方式下,所有的action类型都会自动生成,完全不需要手动处理联合类型和类型收窄的问题。
内容的提问来源于stack exchange,提问作者Evanss
相关产品推荐
相关产品推荐

