TypeScript+Redux Toolkit:createSlice生成action调用出现冲突TS2554参数错误
问题场景
在 TypeScript 与 Redux Toolkit 技术栈下,调用 createSlice 创建的 action 时,会触发两类互相冲突的 TS2554 报错:「期望0个参数但实际传入1个」、「期望1个参数但实际传入0个」。
复现代码
export interface UffStatusState { fetchingPickStatus: APIState; activeMerchant: string | null; displayPicker: boolean | false; } const uffStatusSlice = createSlice({ name: "uffStatus", initialState: { fetchingPickStatus: APIState.idle, activeMerchant: null, displayPicker: false, } as UffStatusState, reducers: { setfetchingPickStatus: (state, fetchingAction: PayloadAction<APIState>) => { state.fetchingPickStatus = fetchingAction.payload; }, setActiveMerchant: (state, activeMerchantAction: PayloadAction<string>) => { state.activeMerchant = activeMerchantAction.payload; }, setDisplayPicker: (state, display: PayloadAction<boolean>) => { state.displayPicker = display.payload }, } }); export const { setfetchingPickStatus, setActiveMerchant, setDisplayPicker } = uffStatusSlice.actions; export default uffStatusSlice.reducer;
具体报错表现
无参数调用
setDisplayPicker时,会同时弹出两条互相冲突的报错:ERROR in src/components/HeaderBar.tsx:14:56 TS2554: Expected 0 arguments, but got 1.
12 |
13 | export default function HeaderBar() {14 | const handleSelectMerchants = () => useStoreDispatch(setDisplayPicker());
| ^^^^^^^^^^^^^^^^^^
15 | return (
16 | <>
17 | <KatRow className={'header'}>ERROR in src/components/HeaderBar.tsx:14:56 TS2554: Expected 1 arguments, but got 0.
12 |
13 | export default function HeaderBar() {14 | const handleSelectMerchants = () => useStoreDispatch(setDisplayPicker());
| ^^^^^^^^^^^^^^^^^^
15 | return (
16 | <>
17 | <KatRow className={'header'}>携带参数(无论传入
true还是false)调用setDisplayPicker时,会统一抛出「期望0个参数但获取到1个」的报错:ERROR in src/components/HeaderBar.tsx:14:56 TS2554: Expected 0 arguments, but got 1.
12 |
13 | export default function HeaderBar() {14 | const handleSelectMerchants = () => useStoreDispatch(setDisplayPicker(true));
| ^^^^^^^^^^^^^^^^^^^^^^
15 | return (
16 | <>
17 | <KatRow className={'header'}>ERROR in src/components/MerchantSelection.tsx:64:42 TS2554: Expected 0 arguments, but got 1.
} const onDismiss = () => useStoreDispatch(setDisplayPicker(false)); ^^^^^^^^^^^^^^^^^^^^^^^ 65 |
66 | export default function MerchantSelection() {
67 | const { displayPicker } = useStoreSelector( (state) => state.uffStoreSetting );
问题根因
RTK 的 createSlice 对 reducer 的类型推导有固定约定:
- 每个 reducer 函数的第一个参数固定为当前状态,不需要手动添加类型注解
- 第二个参数直接代表 action 的 payload 类型,RTK 会自动基于该参数的类型,生成对应 action creator 的入参类型,自动包装出完整的
PayloadAction<T>结构 - 手动给第二个参数添加
PayloadAction<T>类型注解,会破坏 RTK 的类型推导逻辑:一方面会识别到 reducer 存在两个必填形参,推导 action creator 需要传入1个参数;另一方面又因为入参类型不符合内部推导规则,反向将 action creator 识别为无参数要求,最终出现互相冲突的 TS2554 报错。
修正方案
- 移除每个 reducer 第二个参数上的
PayloadAction<T>类型注解,直接将参数类型标注为 payload 本身的原生类型即可:reducers: { setfetchingPickStatus: (state, fetchingValue: APIState) => { state.fetchingPickStatus = fetchingValue; }, setActiveMerchant: (state, activeMerchantValue: string) => { state.activeMerchant = activeMerchantValue; }, setDisplayPicker: (state, displayValue: boolean) => { state.displayPicker = displayValue }, } - 原接口定义中
displayPicker: boolean | false;的写法属于冗余类型定义(false本身就是boolean类型的子集),可简化为displayPicker: boolean;,没有额外的类型约束效果。
调整完成后,RTK 会自动完成全链路类型关联:生成的 setDisplayPicker action creator 会被推导为必须传入1个 boolean 类型参数,调用时的类型校验会完全正常,不会再出现冲突报错。
内容的提问来源于stack exchange,提问作者Happy

