Open
Description
There seems to be a typing issue when using a wrapped createSlice
, the wrapper itself has reducers, and an action has a prepare callback.
The following code demonstrates a problem (most of the code is directly from the documentation).
Here is also a CodeSandbox to demonstrate the issue.
import {
createSlice,
PayloadAction,
SliceCaseReducers,
ValidateSliceCaseReducers,
} from "@reduxjs/toolkit";
interface GenericState<T> {
data?: T;
status: "loading" | "finished" | "error";
}
const createGenericSlice = <
T,
Reducers extends SliceCaseReducers<GenericState<T>>
>({
name = "",
initialState,
reducers,
}: {
name: string;
initialState: GenericState<T>;
reducers: ValidateSliceCaseReducers<GenericState<T>, Reducers>;
}) => {
return createSlice({
name,
initialState,
reducers: {
start(state) {
state.status = "loading";
},
/**
* If you want to write to values of the state that depend on the generic
* (in this case: `state.data`, which is T), you might need to specify the
* State type manually here, as it defaults to `Draft<GenericState<T>>`,
* which can sometimes be problematic with yet-unresolved generics.
* This is a general problem when working with immer's Draft type and generics.
*/
success(state: GenericState<T>, action: PayloadAction<T>) {
state.data = action.payload;
state.status = "finished";
},
...reducers,
},
});
};
const wrappedSlice = createGenericSlice({
name: "test",
initialState: { status: "loading" } as GenericState<string>,
reducers: {
magic(state) {
state.status = "finished";
state.data = "hocus pocus";
},
moreMagic: {
reducer(state, action: PayloadAction<string>) {
state.data = action.payload;
},
prepare(n: number) {
return { payload: String(n) };
},
},
},
});
const { start, success, magic, moreMagic } = wrappedSlice.actions;
success("foo");
moreMagic(333); // <- type error here
The last line shows the error:
The argument of type 'number' is not assigned to the parameter of type 'never'.
And moreMagic
is wrongly typed as:
const moreMagic: ActionCreatorWithPreparedPayload
(...args: never[]) => {
payload: string;
type: `${string}/moreMagic`;
}
Strangely, when the reducers in the wrapper are commented out (start
and success
), the type issue goes away.
"redux": "5.0.1",
"react-redux": "9.0.4",
"@reduxjs/toolkit": "2.0.1"