Skip to content

Broken types of action parameters when using wrapped createSlice and prepare callback #4023

Open
@medihack

Description

@medihack

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"

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions