使用redux-actions时如何为Reducer的Action参数正确类型标注?
解决redux-actions createAction与Reducer的Action类型标注问题
我帮你搞定这个TypeScript类型标注的问题,咱们一步步来分析和解决:
问题根源
你遇到的核心问题是TypeScript无法在switch case中正确窄化Action的类型。这是因为你手动定义的Action接口中的type是自定义类型别名(比如AddItems),而createAction返回的Action的type实际是字符串字面量,两者没有被TypeScript正确关联,导致联合类型ShoppingCartActions无法通过type字段区分不同的Action,最终payload的类型无法被准确推断。
解决方案:自动推导Action类型(推荐)
redux-actions提供了ActionType工具类型,可以直接从你的action creators对象中自动推导正确的联合类型,完全不需要手动编写每个Action接口,既简洁又能避免类型不匹配的问题。
重构后的完整代码
import { createAction, ActionType } from 'redux-actions'; import { Action } from 'redux'; // 假设你的基础类型已定义,这里补充示例 interface CartItem { item: { id: number }; // 其他购物车商品属性... } interface ShoppingCartStore { cartItems: CartItem[]; } const shoppingCartInitialState: ShoppingCartStore = { cartItems: [] }; // 用as const确保action type是字面量类型,让TypeScript能准确区分 const ADD_ITEMS = 'addItems' as const; const REMOVE_ITEM = 'removeItem' as const; // 创建action creators,TypeScript会自动推导返回的Action类型 export const addItems = createAction<CartItem>(ADD_ITEMS); export const removeItem = createAction<number>(REMOVE_ITEM); // 从action creators对象自动生成联合类型 export const shoppingCartActions = { addItems, removeItem }; export type ShoppingCartActions = ActionType<typeof shoppingCartActions>; export default function shoppingCartReducer( state: ShoppingCartStore = shoppingCartInitialState, action: ShoppingCartActions ) { switch (action.type) { case ADD_ITEMS: // 这里action会被自动窄化为对应类型,payload是CartItem,无需非空断言 return { cartItems: [ ...state.cartItems.filter(cartItem => cartItem.item.id !== action.payload.item.id), action.payload ] }; case REMOVE_ITEM: // 这里action被窄化为对应类型,payload是number return { cartItems: state.cartItems.filter(cartItem => cartItem.item.id !== action.payload) }; default: return state; } }
关键改进点
- 字面量类型的Action Type:用
as const定义ADD_ITEMS和REMOVE_ITEM,让它们成为不可变的字面量类型,这样TypeScript在switch case中能精准识别每个case对应的Action类型。 - 自动推导联合类型:使用
ActionType<typeof shoppingCartActions>从action creators对象中自动生成联合类型,完全替代手动编写的AddItemAction、RemoveItemAction,避免了手动定义的类型偏差。 - 无需非空断言:类型窄化后,TypeScript能确定每个case中
payload的类型和存在性,再也不用payload!这种不安全的非空断言。
备选方案:手动定义Action接口(如果偏好手动控制)
如果你坚持要手动编写Action接口,需要确保type字段使用字面量类型,而不是自定义类型别名:
const ADD_ITEMS = 'addItems' as const; type AddItemsType = typeof ADD_ITEMS; interface AddItemAction extends Action<CartItem> { type: AddItemsType; // 使用字面量类型而非自定义别名 } const REMOVE_ITEM = 'removeItem' as const; type RemoveItemType = typeof REMOVE_ITEM; interface RemoveItemAction extends Action<number> { type: RemoveItemType; } export type ShoppingCartActions = AddItemAction | RemoveItemAction;
这样switch case同样能正确窄化Action类型,解决payload的类型报错问题。
内容的提问来源于stack exchange,提问作者Jstuff
相关产品推荐
相关产品推荐

