TypeScript+useReducer购物车报错:参数类型不匹配求助
我正在学习TypeScript,开发了一个简易购物车应用,使用useReducer时遇到如下错误:
No overload matches this call.
Overload 1 of 5, '(reducer: ReducerWithoutAction, initializerArg: any, initializer?: undefined): [any, DispatchWithoutAction]', gave the following error.
Argument of type '(state: ShoppingInitialState, action: shoppingReducer) => { cart: (ProductInterface | undefined)[]; products: ProductInterface[]; }' is not assignable to parameter of type 'ReducerWithoutAction'.
Overload 2 of 5, '(reducer: (state: ShoppingInitialState, action: shoppingReducer) => { cart: (ProductInterface | undefined)[]; products: ProductInterface[]; }, initialState: never, initializer?: undefined): [...]', gave the following error.
Argument of type 'ShoppingInitialState' is not assignable to parameter of type 'never'
在shopping.reducer.ts中执行添加商品到购物车的代码(cart: [...state.cart, addCartItem])时,错误表现更明显,以下是问题分析及解决方法:
相关代码细节
shopping.interface.ts(接口定义)
export enum ActionsTypes { ADD_TO_CART = 'ADD_TO_CART', REMOVE_ONE_FROM_CART = 'REMOVE_ONE_FROM_CART', REMOVE_ALL_FROM_CART = 'REMOVE_ALL_FROM_CART', CLEAN_CART = 'CLEAN_CART', } export interface shoppingReducer { type: ActionsTypes; payload: number; } export interface ShoppingInitialState { products: Array<ProductInterface>; cart: Array<ProductInterface>; } interface ProductInterface { id: number; image: string; name: string; price: number; }
shopping.reducer.ts(Reducer实现)
import { ACTIONS } from '@/actions/shopping.actions'; import { ShoppingInitialState, shoppingReducer, } from '@/models/shopping.interface'; export const shoppingInitialState: ShoppingInitialState = { products: [ { id: 1, image: 'https://placeimg.com/200/200/tech/1', name: 'Product 1', price: 100, }, { id: 2, image: 'https://placeimg.com/200/200/tech/2', name: 'Product 2', price: 200, }, { id: 3, image: 'https://placeimg.com/200/200/tech/3', name: 'Product 3', price: 100, }, { id: 4, image: 'https://placeimg.com/200/200/tech/4', name: 'Product 4', price: 400, }, { id: 5, image: 'https://placeimg.com/200/200/tech/5', name: 'Product 5', price: 100, }, { id: 6, image: 'https://placeimg.com/200/200/tech/6', name: 'Product 6', price: 600, }, ], cart: [], }; export function shoppingReducer( state: ShoppingInitialState, action: shoppingReducer ) { const { type, payload } = action; switch (type) { case ACTIONS.ADD_TO_CART: { let addCartItem = state.products.find( (product) => product.id === payload ); return { ...state, cart: [...state.cart, addCartItem], }; } case ACTIONS.REMOVE_ONE_FROM_CART: { } case ACTIONS.REMOVE_ONE_FROM_CART: { } case ACTIONS.CLEAN_CART: { } default: return state; } }
组件说明
- Cart.tsx:通过useReducer获取购物车状态并渲染
- Products.tsx:展示商品列表,触发添加到购物车的dispatch操作
- App.tsx:整合Products和Cart组件
问题原因
- 命名冲突:接口
shoppingReducer与函数shoppingReducer同名,TypeScript无法正确推断类型,导致useReducer无法识别合法的Reducer类型。 - 类型不兼容:
state.products.find()可能返回undefined(当找不到对应ID的商品时),但cart的类型是Array<ProductInterface>,不能包含undefined,导致返回的state类型不符合定义。 - 代码逻辑缺陷:
REMOVE_ONE_FROM_CART分支重复定义,且部分分支无返回值,干扰TypeScript的类型推断。
解决步骤
1. 修正命名冲突
将接口shoppingReducer重命名为ShoppingAction,避免与函数名冲突:
// shopping.interface.ts export interface ShoppingAction { type: ActionsTypes; payload: number; }
更新reducer中的类型引用:
// shopping.reducer.ts import { ShoppingInitialState, ShoppingAction, // 替换原shoppingReducer接口 } from '@/models/shopping.interface'; export function shoppingReducer( state: ShoppingInitialState, action: ShoppingAction // 更新参数类型 ) { // ... 原有逻辑 }
2. 处理find返回undefined的情况
确保添加到购物车的商品一定存在,过滤无效添加:
case ACTIONS.ADD_TO_CART: { const addCartItem = state.products.find( (product) => product.id === payload ); // 商品不存在则直接返回原状态 if (!addCartItem) return state; return { ...state, cart: [...state.cart, addCartItem], }; }
3. 修复重复分支与补充逻辑
删除重复的REMOVE_ONE_FROM_CART分支,并补充各分支的完整逻辑:
switch (type) { case ACTIONS.ADD_TO_CART: { // ... 上述处理逻辑 } case ACTIONS.REMOVE_ONE_FROM_CART: { return { ...state, cart: state.cart.filter(item => item.id !== payload) }; } case ACTIONS.REMOVE_ALL_FROM_CART: { return { ...state, cart: state.cart.filter(item => item.id !== payload) }; } case ACTIONS.CLEAN_CART: { return { ...state, cart: [] }; } default: return state; }
4. 明确useReducer的类型
在组件中使用useReducer时,显式指定状态与action类型:
import { useReducer } from 'react'; import { shoppingReducer, shoppingInitialState } from '@/reducers/shopping.reducer'; import { ShoppingInitialState, ShoppingAction } from '@/models/shopping.interface'; const [state, dispatch] = useReducer<(state: ShoppingInitialState, action: ShoppingAction) => ShoppingInitialState>( shoppingReducer, shoppingInitialState );
内容的提问来源于stack exchange,提问作者Cesar Gomez

