From 6d3ea8910f24bf3a5ce883e70cb2b3c5ff822f22 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.j.durrant@gmail.com>
Date: Sat, 18 Nov 2023 18:17:45 +0000
Subject: [PATCH 001/178] Custom slice creator implementation

---
 packages/toolkit/src/createSlice.ts           | 773 ++++++++++--------
 .../toolkit/src/tests/createSlice.test.ts     |   7 +-
 .../toolkit/src/tests/createSlice.typetest.ts |  16 +-
 .../toolkit/src/tests/tsconfig.typetests.json |   4 +-
 packages/toolkit/src/tsHelpers.ts             |   4 +
 packages/toolkit/tsconfig.base.json           |  42 +-
 packages/toolkit/tsconfig.json                |   4 +-
 7 files changed, 477 insertions(+), 373 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 0057873f8e..33accd1e99 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -16,7 +16,12 @@ import type {
 import { createReducer } from './createReducer'
 import type { ActionReducerMapBuilder, TypedActionCreator } from './mapBuilders'
 import { executeReducerBuilderCallback } from './mapBuilders'
-import type { Id, Tail, TypeGuard } from './tsHelpers'
+import type {
+  Id,
+  KeysMatching,
+  TypeGuard,
+  UnionToIntersection,
+} from './tsHelpers'
 import type { InjectConfig } from './combineSlices'
 import type {
   AsyncThunk,
@@ -28,12 +33,242 @@ import type {
 import { createAsyncThunk as _createAsyncThunk } from './createAsyncThunk'
 import { emplace } from './utils'
 
-const asyncThunkSymbol = Symbol.for('rtk-slice-createasyncthunk')
-// type is annotated because it's too long to infer
-export const asyncThunkCreator: {
-  [asyncThunkSymbol]: typeof _createAsyncThunk
-} = {
-  [asyncThunkSymbol]: _createAsyncThunk,
+export enum ReducerType {
+  reducer = 'reducer',
+  reducerWithPrepare = 'reducerWithPrepare',
+  asyncThunk = 'asyncThunk',
+}
+
+export interface ReducerTypes extends Record<ReducerType, true> {}
+
+export type RegisteredReducerType = KeysMatching<ReducerTypes, true>
+
+interface ReducerDefinition<
+  T extends RegisteredReducerType = RegisteredReducerType
+> {
+  _reducerDefinitionType: T
+}
+
+export interface SliceReducerCreators<
+  State = any,
+  CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
+  Name extends string = string
+> {
+  [ReducerType.reducer]: {
+    create(
+      caseReducer: CaseReducer<State, PayloadAction>
+    ): CaseReducerDefinition<State, PayloadAction>
+    create<Payload = any>(
+      caseReducer: CaseReducer<State, PayloadAction<Payload>>
+    ): CaseReducerDefinition<State, PayloadAction<Payload>>
+    actions: {
+      [ReducerName in keyof CaseReducers as CaseReducers[ReducerName] extends CaseReducer<
+        State,
+        any
+      >
+        ? ReducerName
+        : never]: ActionCreatorForCaseReducer<
+        CaseReducers[ReducerName],
+        SliceActionType<Name, ReducerName>
+      >
+    }
+    caseReducers: {
+      [ReducerName in keyof CaseReducers as CaseReducers[ReducerName] extends CaseReducer<
+        State,
+        any
+      >
+        ? ReducerName
+        : never]: CaseReducers[ReducerName]
+    }
+  }
+  [ReducerType.reducerWithPrepare]: {
+    create<Prepare extends PrepareAction<any>>(
+      prepare: Prepare,
+      reducer: CaseReducer<
+        State,
+        ReturnType<_ActionCreatorWithPreparedPayload<Prepare>>
+      >
+    ): {
+      _reducerDefinitionType: ReducerType.reducerWithPrepare
+      prepare: Prepare
+      reducer: CaseReducer<
+        State,
+        ReturnType<_ActionCreatorWithPreparedPayload<Prepare>>
+      >
+    }
+    actions: {
+      [ReducerName in keyof CaseReducers as CaseReducers[ReducerName] extends CaseReducerWithPrepare<
+        State,
+        any
+      >
+        ? ReducerName
+        : never]: CaseReducers[ReducerName] extends { prepare: any }
+        ? ActionCreatorForCaseReducerWithPrepare<
+            CaseReducers[ReducerName],
+            SliceActionType<Name, ReducerName>
+          >
+        : never
+    }
+    caseReducers: {
+      [ReducerName in keyof CaseReducers as CaseReducers[ReducerName] extends CaseReducerWithPrepare<
+        State,
+        any
+      >
+        ? ReducerName
+        : never]: CaseReducers[ReducerName] extends { reducer: infer Reducer }
+        ? Reducer
+        : never
+    }
+  }
+  [ReducerType.asyncThunk]: {
+    create: AsyncThunkCreator<State>
+    actions: {
+      [ReducerName in keyof CaseReducers as CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition<
+        State,
+        any,
+        any,
+        any
+      >
+        ? ReducerName
+        : never]: CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition<
+        any,
+        infer ThunkArg,
+        infer Returned,
+        infer ThunkApiConfig
+      >
+        ? AsyncThunk<Returned, ThunkArg, ThunkApiConfig>
+        : never
+    }
+    caseReducers: {
+      [ReducerName in keyof CaseReducers as CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition<
+        State,
+        any,
+        any,
+        any
+      >
+        ? ReducerName
+        : never]: CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition<
+        State,
+        any,
+        any,
+        any
+      >
+        ? Id<
+            Pick<
+              Required<CaseReducers[ReducerName]>,
+              'fulfilled' | 'rejected' | 'pending' | 'settled'
+            >
+          >
+        : never
+    }
+  }
+}
+
+export type ReducerCreators<
+  State,
+  CreatorMap extends Record<string, RegisteredReducerType>
+> = {
+  reducer: SliceReducerCreators<State>[ReducerType.reducer]['create']
+  preparedReducer: SliceReducerCreators<State>[ReducerType.reducerWithPrepare]['create']
+} & {
+  [Name in keyof CreatorMap]: SliceReducerCreators<State>[CreatorMap[Name]]['create']
+}
+
+interface ReducerHandlingContext<State> {
+  sliceCaseReducersByType: Record<string, CaseReducer<State, any>>
+  sliceMatchers: ActionMatcherDescriptionCollection<State>
+
+  sliceCaseReducersByName: Record<string, any>
+  actionCreators: Record<string, any>
+}
+
+interface ReducerHandlingContextMethods<
+  State,
+  ReducerType extends RegisteredReducerType = RegisteredReducerType
+> {
+  /**
+   * Adds a case reducer to handle a single action type.
+   * @param actionCreator - Either a plain action type string, or an action creator generated by [`createAction`](./createAction) that can be used to determine the action type.
+   * @param reducer - The actual case reducer function.
+   */
+  addCase<ActionCreator extends TypedActionCreator<string>>(
+    actionCreator: ActionCreator,
+    reducer: CaseReducer<State, ReturnType<ActionCreator>>
+  ): ReducerHandlingContextMethods<State, ReducerType>
+  /**
+   * Adds a case reducer to handle a single action type.
+   * @param actionCreator - Either a plain action type string, or an action creator generated by [`createAction`](./createAction) that can be used to determine the action type.
+   * @param reducer - The actual case reducer function.
+   */
+  addCase<Type extends string, A extends Action<Type>>(
+    type: Type,
+    reducer: CaseReducer<State, A>
+  ): ReducerHandlingContextMethods<State, ReducerType>
+
+  /**
+   * Allows you to match incoming actions against your own filter function instead of only the `action.type` property.
+   * @remarks
+   * If multiple matcher reducers match, all of them will be executed in the order
+   * they were defined in - even if a case reducer already matched.
+   * All calls to `builder.addMatcher` must come after any calls to `builder.addCase` and before any calls to `builder.addDefaultCase`.
+   * @param matcher - A matcher function. In TypeScript, this should be a [type predicate](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates)
+   *   function
+   * @param reducer - The actual case reducer function.
+   *
+   */
+  addMatcher<A>(
+    matcher: TypeGuard<A>,
+    reducer: CaseReducer<State, A extends Action ? A : A & Action>
+  ): ReducerHandlingContextMethods<State, ReducerType>
+  /**
+   * Add an action to be exposed under the final `slice.actions` key.
+   * @param name The key to be exposed as.
+   * @param actionCreator The action to expose.
+   * @example
+   * context.exposeAction("addPost", createAction<Post>("addPost"));
+   *
+   * export const { addPost } = slice.actions
+   *
+   * dispatch(addPost(post))
+   */
+  exposeAction(
+    name: string,
+    // TODO: see if there's a way to get the actual type cleanly
+    actionCreator: unknown
+  ): ReducerHandlingContextMethods<State, ReducerType>
+  /**
+   * Add a case reducer to be exposed under the final `slice.caseReducers` key.
+   * @param name The key to be exposed as.
+   * @param reducer The reducer to expose.
+   * @example
+   * context.exposeCaseReducer("addPost", (state, action: PayloadAction<Post>) => {
+   *   state.push(action.payload)
+   * })
+   *
+   * slice.caseReducers.addPost([], addPost(post))
+   */
+  exposeCaseReducer(
+    name: string,
+    // TODO: see if there's a way to get the actual type cleanly
+    reducer: unknown
+  ): ReducerHandlingContextMethods<State, ReducerType>
+}
+
+interface ReducerDetails {
+  /** The key the reducer was defined under */
+  reducerName: string
+  /** The predefined action type, i.e. `${slice.name}/${reducerName}` */
+  type: string
+}
+
+export type ReducerCreator<Type extends RegisteredReducerType> = {
+  type: Type
+  define: SliceReducerCreators[Type]['create']
+  handle<State>(
+    details: ReducerDetails,
+    definition: ReturnType<SliceReducerCreators[Type]['create']>,
+    context: ReducerHandlingContextMethods<State, Type>
+  ): void
 }
 
 interface InjectIntoConfig<NewReducerPath extends string> extends InjectConfig {
@@ -187,7 +422,8 @@ export interface CreateSliceOptions<
   CR extends SliceCaseReducers<State> = SliceCaseReducers<State>,
   Name extends string = string,
   ReducerPath extends string = Name,
-  Selectors extends SliceSelectors<State> = SliceSelectors<State>
+  Selectors extends SliceSelectors<State> = SliceSelectors<State>,
+  CreatorMap extends Record<string, RegisteredReducerType> = {}
 > {
   /**
    * The slice's name. Used to namespace the generated action types.
@@ -211,7 +447,7 @@ export interface CreateSliceOptions<
    */
   reducers:
     | ValidateSliceCaseReducers<State, CR>
-    | ((creators: ReducerCreators<State>) => CR)
+    | ((creators: ReducerCreators<State, CreatorMap>) => CR)
 
   /**
    * A callback that receives a *builder* object to define
@@ -263,16 +499,6 @@ createSlice({
   selectors?: Selectors
 }
 
-export enum ReducerType {
-  reducer = 'reducer',
-  reducerWithPrepare = 'reducerWithPrepare',
-  asyncThunk = 'asyncThunk',
-}
-
-interface ReducerDefinition<T extends ReducerType = ReducerType> {
-  _reducerDefinitionType: T
-}
-
 export interface CaseReducerDefinition<
   S = any,
   A extends Action = UnknownAction
@@ -393,47 +619,13 @@ interface AsyncThunkCreator<
   >
 }
 
-export interface ReducerCreators<State> {
-  reducer(
-    caseReducer: CaseReducer<State, PayloadAction>
-  ): CaseReducerDefinition<State, PayloadAction>
-  reducer<Payload>(
-    caseReducer: CaseReducer<State, PayloadAction<Payload>>
-  ): CaseReducerDefinition<State, PayloadAction<Payload>>
-
-  asyncThunk: AsyncThunkCreator<State>
-
-  preparedReducer<Prepare extends PrepareAction<any>>(
-    prepare: Prepare,
-    reducer: CaseReducer<
-      State,
-      ReturnType<_ActionCreatorWithPreparedPayload<Prepare>>
-    >
-  ): {
-    _reducerDefinitionType: ReducerType.reducerWithPrepare
-    prepare: Prepare
-    reducer: CaseReducer<
-      State,
-      ReturnType<_ActionCreatorWithPreparedPayload<Prepare>>
-    >
-  }
-}
-
 /**
  * The type describing a slice's `reducers` option.
  *
  * @public
  */
 export type SliceCaseReducers<State> =
-  | Record<
-      string,
-      | CaseReducerDefinition<State, PayloadAction<any>>
-      | CaseReducerWithPrepareDefinition<
-          State,
-          PayloadAction<any, string, any, any>
-        >
-      | AsyncThunkSliceReducerDefinition<State, any, any, any>
-    >
+  | Record<string, ReducerDefinition>
   | Record<
       string,
       | CaseReducer<State, PayloadAction<any>>
@@ -460,31 +652,15 @@ type SliceActionType<
 export type CaseReducerActions<
   CaseReducers extends SliceCaseReducers<any>,
   SliceName extends string
-> = {
-  [Type in keyof CaseReducers]: CaseReducers[Type] extends infer Definition
-    ? Definition extends { prepare: any }
-      ? ActionCreatorForCaseReducerWithPrepare<
-          Definition,
-          SliceActionType<SliceName, Type>
-        >
-      : Definition extends AsyncThunkSliceReducerDefinition<
-          any,
-          infer ThunkArg,
-          infer Returned,
-          infer ThunkApiConfig
-        >
-      ? AsyncThunk<Returned, ThunkArg, ThunkApiConfig>
-      : Definition extends { reducer: any }
-      ? ActionCreatorForCaseReducer<
-          Definition['reducer'],
-          SliceActionType<SliceName, Type>
-        >
-      : ActionCreatorForCaseReducer<
-          Definition,
-          SliceActionType<SliceName, Type>
-        >
-    : never
-}
+> = Id<
+  UnionToIntersection<
+    SliceReducerCreators<
+      any,
+      CaseReducers,
+      SliceName
+    >[RegisteredReducerType]['actions']
+  >
+>
 
 /**
  * Get a `PayloadActionCreator` type for a passed `CaseReducerWithPrepare`
@@ -516,22 +692,15 @@ type ActionCreatorForCaseReducer<CR, Type extends string> = CR extends (
  *
  * @internal
  */
-type SliceDefinedCaseReducers<CaseReducers extends SliceCaseReducers<any>> = {
-  [Type in keyof CaseReducers]: CaseReducers[Type] extends infer Definition
-    ? Definition extends AsyncThunkSliceReducerDefinition<any, any, any, any>
-      ? Id<
-          Pick<
-            Required<Definition>,
-            'fulfilled' | 'rejected' | 'pending' | 'settled'
-          >
-        >
-      : Definition extends {
-          reducer: infer Reducer
-        }
-      ? Reducer
-      : Definition
-    : never
-}
+type SliceDefinedCaseReducers<CaseReducers extends SliceCaseReducers<any>> = Id<
+  UnionToIntersection<
+    SliceReducerCreators<
+      any,
+      CaseReducers,
+      any
+    >[RegisteredReducerType]['caseReducers']
+  >
+>
 
 type RemappedSelector<S extends Selector, NewState> = S extends Selector<
   any,
@@ -587,14 +756,130 @@ function getType(slice: string, actionKey: string): string {
   return `${slice}/${actionKey}`
 }
 
-interface BuildCreateSliceConfig {
+const reducerCreator: ReducerCreator<ReducerType.reducer> = {
+  type: ReducerType.reducer,
+  define(caseReducer: CaseReducer<any, any>) {
+    return Object.assign(
+      {
+        // hack so the wrapping function has the same name as the original
+        // we need to create a wrapper so the `reducerDefinitionType` is not assigned to the original
+        [caseReducer.name](...args: Parameters<typeof caseReducer>) {
+          return caseReducer(...args)
+        },
+      }[caseReducer.name],
+      {
+        _reducerDefinitionType: ReducerType.reducer,
+      } as const
+    )
+  },
+  handle({ type, reducerName }, reducer, context) {
+    context
+      .addCase(type, reducer)
+      .exposeCaseReducer(reducerName, reducer)
+      .exposeAction(reducerName, createAction(type))
+  },
+}
+
+const preparedReducerCreator: ReducerCreator<ReducerType.reducerWithPrepare> = {
+  type: ReducerType.reducerWithPrepare,
+  define(prepare, reducer) {
+    return {
+      _reducerDefinitionType: ReducerType.reducerWithPrepare,
+      prepare,
+      reducer,
+    }
+  },
+  handle({ type, reducerName }, { prepare, reducer }, context) {
+    context
+      .addCase(type, reducer)
+      .exposeCaseReducer(reducerName, reducer)
+      .exposeAction(reducerName, createAction(type, prepare))
+  },
+}
+
+export const asyncThunkCreator: ReducerCreator<ReducerType.asyncThunk> = {
+  type: ReducerType.asyncThunk,
+  define: /* @__PURE__ */ (() => {
+    function asyncThunk(
+      payloadCreator: AsyncThunkPayloadCreator<any, any>,
+      config: AsyncThunkSliceReducerConfig<any, any>
+    ): AsyncThunkSliceReducerDefinition<any, any> {
+      return {
+        _reducerDefinitionType: ReducerType.asyncThunk,
+        payloadCreator,
+        ...config,
+      }
+    }
+    asyncThunk.withTypes = () => asyncThunk
+    return asyncThunk as AsyncThunkCreator<any>
+  })(),
+  handle({ type, reducerName }, definition, context) {
+    const { payloadCreator, fulfilled, pending, rejected, settled, options } =
+      definition
+    const thunk = _createAsyncThunk(type, payloadCreator, options as any)
+    context.exposeAction(reducerName, thunk)
+
+    if (fulfilled) {
+      context.addCase(thunk.fulfilled, fulfilled)
+    }
+    if (pending) {
+      context.addCase(thunk.pending, pending)
+    }
+    if (rejected) {
+      context.addCase(thunk.rejected, rejected)
+    }
+    if (settled) {
+      context.addMatcher(thunk.settled, settled)
+    }
+
+    context.exposeCaseReducer(reducerName, {
+      fulfilled: fulfilled || noop,
+      pending: pending || noop,
+      rejected: rejected || noop,
+      settled: settled || noop,
+    })
+  },
+}
+
+function noop() {}
+
+interface BuildCreateSliceConfig<
+  CreatorMap extends Record<string, RegisteredReducerType>
+> {
   creators?: {
-    asyncThunk?: typeof asyncThunkCreator
-  }
+    [Name in keyof CreatorMap]: ReducerCreator<CreatorMap[Name]>
+  } &
+    Partial<Record<'reducer' | 'preparedReducer', never>>
 }
 
-export function buildCreateSlice({ creators }: BuildCreateSliceConfig = {}) {
-  const cAT = creators?.asyncThunk?.[asyncThunkSymbol]
+export function buildCreateSlice<
+  CreatorMap extends Record<string, RegisteredReducerType> = {}
+>({ creators = {} as any }: BuildCreateSliceConfig<CreatorMap> = {}) {
+  const definers: Record<
+    string,
+    ReducerCreator<RegisteredReducerType>['define']
+  > = {
+    reducer: reducerCreator.define,
+    preparedReducer: preparedReducerCreator.define,
+  }
+  const handlers: Partial<
+    Record<
+      RegisteredReducerType,
+      ReducerCreator<RegisteredReducerType>['handle']
+    >
+  > = {
+    [ReducerType.reducer]: reducerCreator.handle,
+    [ReducerType.reducerWithPrepare]: preparedReducerCreator.handle,
+  }
+  for (const [name, creator] of Object.entries<
+    ReducerCreator<RegisteredReducerType>
+  >(creators)) {
+    if (name === 'reducer' || name === 'preparedReducer') {
+      throw new Error('Cannot use reserved creator name: ' + name)
+    }
+    definers[name] = creator.define
+    handlers[creator.type] = creator.handle
+  }
   return function createSlice<
     State,
     CaseReducers extends SliceCaseReducers<State>,
@@ -607,7 +892,8 @@ export function buildCreateSlice({ creators }: BuildCreateSliceConfig = {}) {
       CaseReducers,
       Name,
       ReducerPath,
-      Selectors
+      Selectors,
+      CreatorMap
     >
   ): Slice<State, CaseReducers, Name, ReducerPath, Selectors> {
     const { name, reducerPath = name as unknown as ReducerPath } = options
@@ -626,13 +912,6 @@ export function buildCreateSlice({ creators }: BuildCreateSliceConfig = {}) {
       }
     }
 
-    const reducers =
-      (typeof options.reducers === 'function'
-        ? options.reducers(buildReducerCreators<State>())
-        : options.reducers) || {}
-
-    const reducerNames = Object.keys(reducers)
-
     const context: ReducerHandlingContext<State> = {
       sliceCaseReducersByName: {},
       sliceCaseReducersByType: {},
@@ -677,28 +956,43 @@ export function buildCreateSlice({ creators }: BuildCreateSliceConfig = {}) {
       },
     }
 
-    reducerNames.forEach((reducerName) => {
-      const reducerDefinition = reducers[reducerName]
-      const reducerDetails: ReducerDetails = {
-        reducerName,
-        type: getType(name, reducerName),
-        createNotation: typeof options.reducers === 'function',
+    if (typeof options.reducers === 'function') {
+      const reducers = options.reducers(definers as any)
+      for (const [reducerName, reducerDefinition] of Object.entries(reducers)) {
+        const { _reducerDefinitionType: type } = reducerDefinition
+        const handler = handlers[type as RegisteredReducerType]
+        if (!handler) {
+          throw new Error('Unsupported reducer type: ' + type)
+        }
+        const reducerDetails: ReducerDetails = {
+          reducerName,
+          type: getType(name, reducerName),
+        }
+        handler(reducerDetails, reducerDefinition, contextMethods)
       }
-      if (isAsyncThunkSliceReducerDefinition<State>(reducerDefinition)) {
-        handleThunkCaseReducerDefinition(
-          reducerDetails,
-          reducerDefinition,
-          contextMethods,
-          cAT
-        )
-      } else {
-        handleNormalReducerDefinition<State>(
-          reducerDetails,
-          reducerDefinition,
-          contextMethods
-        )
+    } else {
+      for (const [reducerName, reducerDefinition] of Object.entries(
+        options.reducers
+      )) {
+        const reducerDetails: ReducerDetails = {
+          reducerName,
+          type: getType(name, reducerName),
+        }
+        if ('reducer' in reducerDefinition) {
+          preparedReducerCreator.handle(
+            reducerDetails,
+            reducerDefinition,
+            contextMethods
+          )
+        } else {
+          reducerCreator.handle(
+            reducerDetails,
+            reducerDefinition,
+            contextMethods
+          )
+        }
       }
-    })
+    }
 
     function buildReducer() {
       if (process.env.NODE_ENV !== 'production') {
@@ -724,7 +1018,7 @@ export function buildCreateSlice({ creators }: BuildCreateSliceConfig = {}) {
 
       return createReducer(options.initialState, (builder) => {
         for (let key in finalCaseReducers) {
-          builder.addCase(key, finalCaseReducers[key] as CaseReducer<any>)
+          builder.addCase(key, finalCaseReducers[key] as CaseReducer)
         }
         for (let sM of context.sliceMatchers) {
           builder.addMatcher(sM.matcher, sM.reducer)
@@ -818,7 +1112,7 @@ export function buildCreateSlice({ creators }: BuildCreateSliceConfig = {}) {
 }
 
 function wrapSelector<State, NewState, S extends Selector<State>>(
-  slice: Slice,
+  slice: Slice<State, any>,
   selector: S,
   selectState: Selector<NewState, State>,
   injected?: boolean
@@ -849,222 +1143,3 @@ function wrapSelector<State, NewState, S extends Selector<State>>(
  * @public
  */
 export const createSlice = buildCreateSlice()
-
-interface ReducerHandlingContext<State> {
-  sliceCaseReducersByName: Record<
-    string,
-    | CaseReducer<State, any>
-    | Pick<
-        AsyncThunkSliceReducerDefinition<State, any, any, any>,
-        'fulfilled' | 'rejected' | 'pending' | 'settled'
-      >
-  >
-  sliceCaseReducersByType: Record<string, CaseReducer<State, any>>
-  sliceMatchers: ActionMatcherDescriptionCollection<State>
-  actionCreators: Record<string, Function>
-}
-
-interface ReducerHandlingContextMethods<State> {
-  /**
-   * Adds a case reducer to handle a single action type.
-   * @param actionCreator - Either a plain action type string, or an action creator generated by [`createAction`](./createAction) that can be used to determine the action type.
-   * @param reducer - The actual case reducer function.
-   */
-  addCase<ActionCreator extends TypedActionCreator<string>>(
-    actionCreator: ActionCreator,
-    reducer: CaseReducer<State, ReturnType<ActionCreator>>
-  ): ReducerHandlingContextMethods<State>
-  /**
-   * Adds a case reducer to handle a single action type.
-   * @param actionCreator - Either a plain action type string, or an action creator generated by [`createAction`](./createAction) that can be used to determine the action type.
-   * @param reducer - The actual case reducer function.
-   */
-  addCase<Type extends string, A extends Action<Type>>(
-    type: Type,
-    reducer: CaseReducer<State, A>
-  ): ReducerHandlingContextMethods<State>
-
-  /**
-   * Allows you to match incoming actions against your own filter function instead of only the `action.type` property.
-   * @remarks
-   * If multiple matcher reducers match, all of them will be executed in the order
-   * they were defined in - even if a case reducer already matched.
-   * All calls to `builder.addMatcher` must come after any calls to `builder.addCase` and before any calls to `builder.addDefaultCase`.
-   * @param matcher - A matcher function. In TypeScript, this should be a [type predicate](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates)
-   *   function
-   * @param reducer - The actual case reducer function.
-   *
-   */
-  addMatcher<A>(
-    matcher: TypeGuard<A>,
-    reducer: CaseReducer<State, A extends Action ? A : A & Action>
-  ): ReducerHandlingContextMethods<State>
-  /**
-   * Add an action to be exposed under the final `slice.actions` key.
-   * @param name The key to be exposed as.
-   * @param actionCreator The action to expose.
-   * @example
-   * context.exposeAction("addPost", createAction<Post>("addPost"));
-   *
-   * export const { addPost } = slice.actions
-   *
-   * dispatch(addPost(post))
-   */
-  exposeAction(
-    name: string,
-    actionCreator: Function
-  ): ReducerHandlingContextMethods<State>
-  /**
-   * Add a case reducer to be exposed under the final `slice.caseReducers` key.
-   * @param name The key to be exposed as.
-   * @param reducer The reducer to expose.
-   * @example
-   * context.exposeCaseReducer("addPost", (state, action: PayloadAction<Post>) => {
-   *   state.push(action.payload)
-   * })
-   *
-   * slice.caseReducers.addPost([], addPost(post))
-   */
-  exposeCaseReducer(
-    name: string,
-    reducer:
-      | CaseReducer<State, any>
-      | Pick<
-          AsyncThunkSliceReducerDefinition<State, any, any, any>,
-          'fulfilled' | 'rejected' | 'pending' | 'settled'
-        >
-  ): ReducerHandlingContextMethods<State>
-}
-
-interface ReducerDetails {
-  /** The key the reducer was defined under */
-  reducerName: string
-  /** The predefined action type, i.e. `${slice.name}/${reducerName}` */
-  type: string
-  /** Whether create. notation was used when defining reducers */
-  createNotation: boolean
-}
-
-function buildReducerCreators<State>(): ReducerCreators<State> {
-  function asyncThunk(
-    payloadCreator: AsyncThunkPayloadCreator<any, any>,
-    config: AsyncThunkSliceReducerConfig<State, any>
-  ): AsyncThunkSliceReducerDefinition<State, any> {
-    return {
-      _reducerDefinitionType: ReducerType.asyncThunk,
-      payloadCreator,
-      ...config,
-    }
-  }
-  asyncThunk.withTypes = () => asyncThunk
-  return {
-    reducer(caseReducer: CaseReducer<State, any>) {
-      return Object.assign(
-        {
-          // hack so the wrapping function has the same name as the original
-          // we need to create a wrapper so the `reducerDefinitionType` is not assigned to the original
-          [caseReducer.name](...args: Parameters<typeof caseReducer>) {
-            return caseReducer(...args)
-          },
-        }[caseReducer.name],
-        {
-          _reducerDefinitionType: ReducerType.reducer,
-        } as const
-      )
-    },
-    preparedReducer(prepare, reducer) {
-      return {
-        _reducerDefinitionType: ReducerType.reducerWithPrepare,
-        prepare,
-        reducer,
-      }
-    },
-    asyncThunk: asyncThunk as any,
-  }
-}
-
-function handleNormalReducerDefinition<State>(
-  { type, reducerName, createNotation }: ReducerDetails,
-  maybeReducerWithPrepare:
-    | CaseReducer<State, { payload: any; type: string }>
-    | CaseReducerWithPrepare<State, PayloadAction<any, string, any, any>>,
-  context: ReducerHandlingContextMethods<State>
-) {
-  let caseReducer: CaseReducer<State, any>
-  let prepareCallback: PrepareAction<any> | undefined
-  if ('reducer' in maybeReducerWithPrepare) {
-    if (
-      createNotation &&
-      !isCaseReducerWithPrepareDefinition(maybeReducerWithPrepare)
-    ) {
-      throw new Error(
-        'Please use the `create.preparedReducer` notation for prepared action creators with the `create` notation.'
-      )
-    }
-    caseReducer = maybeReducerWithPrepare.reducer
-    prepareCallback = maybeReducerWithPrepare.prepare
-  } else {
-    caseReducer = maybeReducerWithPrepare
-  }
-  context
-    .addCase(type, caseReducer)
-    .exposeCaseReducer(reducerName, caseReducer)
-    .exposeAction(
-      reducerName,
-      prepareCallback ? createAction(type, prepareCallback) : createAction(type)
-    )
-}
-
-function isAsyncThunkSliceReducerDefinition<State>(
-  reducerDefinition: any
-): reducerDefinition is AsyncThunkSliceReducerDefinition<State, any, any, any> {
-  return reducerDefinition._reducerDefinitionType === ReducerType.asyncThunk
-}
-
-function isCaseReducerWithPrepareDefinition<State>(
-  reducerDefinition: any
-): reducerDefinition is CaseReducerWithPrepareDefinition<State, any> {
-  return (
-    reducerDefinition._reducerDefinitionType === ReducerType.reducerWithPrepare
-  )
-}
-
-function handleThunkCaseReducerDefinition<State>(
-  { type, reducerName }: ReducerDetails,
-  reducerDefinition: AsyncThunkSliceReducerDefinition<State, any, any, any>,
-  context: ReducerHandlingContextMethods<State>,
-  cAT: typeof _createAsyncThunk | undefined
-) {
-  if (!cAT) {
-    throw new Error(
-      'Cannot use `create.asyncThunk` in the built-in `createSlice`. ' +
-        'Use `buildCreateSlice({ creators: { asyncThunk: asyncThunkCreator } })` to create a customised version of `createSlice`.'
-    )
-  }
-  const { payloadCreator, fulfilled, pending, rejected, settled, options } =
-    reducerDefinition
-  const thunk = cAT(type, payloadCreator, options as any)
-  context.exposeAction(reducerName, thunk)
-
-  if (fulfilled) {
-    context.addCase(thunk.fulfilled, fulfilled)
-  }
-  if (pending) {
-    context.addCase(thunk.pending, pending)
-  }
-  if (rejected) {
-    context.addCase(thunk.rejected, rejected)
-  }
-  if (settled) {
-    context.addMatcher(thunk.settled, settled)
-  }
-
-  context.exposeCaseReducer(reducerName, {
-    fulfilled: fulfilled || noop,
-    pending: pending || noop,
-    rejected: rejected || noop,
-    settled: settled || noop,
-  })
-}
-
-function noop() {}
diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index 1878cf7765..efcb0fcf8d 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -584,10 +584,11 @@ describe('createSlice', () => {
         createSlice({
           name: 'test',
           initialState: [] as any[],
+          // @ts-expect-error asyncThunk not in creators
           reducers: (create) => ({ thunk: create.asyncThunk(() => {}) }),
         })
       ).toThrowErrorMatchingInlineSnapshot(
-        '"Cannot use `create.asyncThunk` in the built-in `createSlice`. Use `buildCreateSlice({ creators: { asyncThunk: asyncThunkCreator } })` to create a customised version of `createSlice`."'
+        '"create.asyncThunk is not a function"'
       )
     })
     const createThunkSlice = buildCreateSlice({
@@ -825,9 +826,7 @@ describe('createSlice', () => {
             },
           }),
         })
-      ).toThrowErrorMatchingInlineSnapshot(
-        `"Please use the \`create.preparedReducer\` notation for prepared action creators with the \`create\` notation."`
-      )
+      ).toThrowErrorMatchingInlineSnapshot('"Unsupported reducer type: undefined"')
     })
   })
 })
diff --git a/packages/toolkit/src/tests/createSlice.typetest.ts b/packages/toolkit/src/tests/createSlice.typetest.ts
index 40488d7e9d..310464713a 100644
--- a/packages/toolkit/src/tests/createSlice.typetest.ts
+++ b/packages/toolkit/src/tests/createSlice.typetest.ts
@@ -594,7 +594,11 @@ const value = actionCreators.anyKey
     cause: string
   }
 
-  const slice = createSlice({
+  const createSliceWithAsyncThunk = buildCreateSlice({
+    creators: { asyncThunk: asyncThunkCreator },
+  })
+
+  const slice = createSliceWithAsyncThunk({
     name: 'test',
     initialState: {} as TestState,
     reducers: (create) => {
@@ -816,7 +820,7 @@ const value = actionCreators.anyKey
   }: {
     name: string
     initialState: GenericState<T>
-    reducers: (create: ReducerCreators<GenericState<T>>) => Reducers
+    reducers: (create: ReducerCreators<GenericState<T>, {}>) => Reducers
   }) => {
     return createSlice({
       name,
@@ -867,9 +871,9 @@ const value = actionCreators.anyKey
  */
 {
   expectExactType(createSlice)(buildCreateSlice())
-  buildCreateSlice({
-    // @ts-expect-error not possible to recreate shape because symbol is not exported
-    creators: { asyncThunk: { [Symbol()]: createAsyncThunk } },
-  })
   buildCreateSlice({ creators: { asyncThunk: asyncThunkCreator } })
+  // @ts-expect-error prevent passing reducer key
+  buildCreateSlice({ creators: { reducer: asyncThunkCreator } })
+  // @ts-expect-error prevent passing preparedReducer key
+  buildCreateSlice({ creators: { preparedReducer: asyncThunkCreator } })
 }
diff --git a/packages/toolkit/src/tests/tsconfig.typetests.json b/packages/toolkit/src/tests/tsconfig.typetests.json
index f63fa81c64..885e291f2f 100644
--- a/packages/toolkit/src/tests/tsconfig.typetests.json
+++ b/packages/toolkit/src/tests/tsconfig.typetests.json
@@ -1,6 +1,6 @@
 {
   "extends": "../../tsconfig.test.json",
   "compilerOptions": {
-    "skipLibCheck": true
+    "skipLibCheck": true,
   }
-}
+}
\ No newline at end of file
diff --git a/packages/toolkit/src/tsHelpers.ts b/packages/toolkit/src/tsHelpers.ts
index f0ed92a6e1..fc6286402c 100644
--- a/packages/toolkit/src/tsHelpers.ts
+++ b/packages/toolkit/src/tsHelpers.ts
@@ -205,3 +205,7 @@ export type Id<T> = { [K in keyof T]: T[K] } & {}
 export type Tail<T extends any[]> = T extends [any, ...infer Tail]
   ? Tail
   : never
+
+export type KeysMatching<T, V> = {
+  [K in keyof T]: T[K] extends V ? K : never
+}[keyof T]
diff --git a/packages/toolkit/tsconfig.base.json b/packages/toolkit/tsconfig.base.json
index dc7dbb02b2..4c20a221f5 100644
--- a/packages/toolkit/tsconfig.base.json
+++ b/packages/toolkit/tsconfig.base.json
@@ -2,7 +2,10 @@
   "compilerOptions": {
     "target": "ESnext",
     "module": "esnext",
-    "lib": ["dom", "esnext"],
+    "lib": [
+      "dom",
+      "esnext"
+    ],
     "importHelpers": true,
     // output .d.ts declaration files for consumers
     "declaration": true,
@@ -32,18 +35,37 @@
     "allowSyntheticDefaultImports": true,
     "emitDeclarationOnly": true,
     "baseUrl": ".",
-    "types": ["vitest/globals"],
+    "types": [
+      "vitest/globals"
+    ],
     "paths": {
-      "@reduxjs/toolkit": ["src/index.ts"], // @remap-prod-remove-line
-      "@reduxjs/toolkit/react": ["src/react/index.ts"], // @remap-prod-remove-line
-      "@reduxjs/toolkit/query": ["src/query/index.ts"], // @remap-prod-remove-line
-      "@reduxjs/toolkit/query/react": ["src/query/react/index.ts"], // @remap-prod-remove-line
+      "@reduxjs/toolkit": [
+        "src/index.ts"
+      ], // @remap-prod-remove-line
+      "@reduxjs/toolkit/react": [
+        "src/react/index.ts"
+      ], // @remap-prod-remove-line
+      "@reduxjs/toolkit/query": [
+        "src/query/index.ts"
+      ], // @remap-prod-remove-line
+      "@reduxjs/toolkit/query/react": [
+        "src/query/react/index.ts"
+      ], // @remap-prod-remove-line
       // for type imports in tests only
-      "@reduxjs/toolkit/dist/*": ["src/*"], // @remap-prod-remove-line
+      "@reduxjs/toolkit/dist/*": [
+        "src/*"
+      ], // @remap-prod-remove-line
       // for type imports in tests only
-      "@reduxjs/toolkit/dist/query/*": ["src/query/*"], // @remap-prod-remove-line
+      "@reduxjs/toolkit/dist/query/*": [
+        "src/query/*"
+      ], // @remap-prod-remove-line
       // internal imports in tests only
-      "@internal/*": ["src/*"]
+      "@internal/*": [
+        "src/*"
+      ],
+      "react": [
+        "../../node_modules/react"
+      ]
     }
   }
-}
+}
\ No newline at end of file
diff --git a/packages/toolkit/tsconfig.json b/packages/toolkit/tsconfig.json
index cb2e851486..63840d5d5d 100644
--- a/packages/toolkit/tsconfig.json
+++ b/packages/toolkit/tsconfig.json
@@ -1,7 +1,7 @@
 {
   "extends": "./tsconfig.base.json",
   "compilerOptions": {
-    "outDir": "dist"
+    "outDir": "dist",
   },
   "include": [
     "src"
@@ -9,5 +9,5 @@
   "exclude": [
     "src/**/*.test.ts*",
     "src/**/tests/*"
-  ]
+  ],
 }
\ No newline at end of file

From ad94c6655adaee0b5cb38b3a1d53a4a965f36433 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.j.durrant@gmail.com>
Date: Sat, 18 Nov 2023 22:41:00 +0000
Subject: [PATCH 002/178] rename KeysMatching and fix formatting on tsconfig

---
 packages/toolkit/src/createSlice.ts | 16 ++++++++------
 packages/toolkit/src/tsHelpers.ts   |  2 +-
 packages/toolkit/tsconfig.base.json | 33 ++++++++---------------------
 3 files changed, 19 insertions(+), 32 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 33accd1e99..02e06356d8 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -18,7 +18,7 @@ import type { ActionReducerMapBuilder, TypedActionCreator } from './mapBuilders'
 import { executeReducerBuilderCallback } from './mapBuilders'
 import type {
   Id,
-  KeysMatching,
+  KeysForValueOfType,
   TypeGuard,
   UnionToIntersection,
 } from './tsHelpers'
@@ -41,7 +41,7 @@ export enum ReducerType {
 
 export interface ReducerTypes extends Record<ReducerType, true> {}
 
-export type RegisteredReducerType = KeysMatching<ReducerTypes, true>
+export type RegisteredReducerType = KeysForValueOfType<ReducerTypes, true>
 
 interface ReducerDefinition<
   T extends RegisteredReducerType = RegisteredReducerType
@@ -854,8 +854,10 @@ interface BuildCreateSliceConfig<
 
 export function buildCreateSlice<
   CreatorMap extends Record<string, RegisteredReducerType> = {}
->({ creators = {} as any }: BuildCreateSliceConfig<CreatorMap> = {}) {
-  const definers: Record<
+>({
+  creators: creatorMap = {} as any,
+}: BuildCreateSliceConfig<CreatorMap> = {}) {
+  const creators: Record<
     string,
     ReducerCreator<RegisteredReducerType>['define']
   > = {
@@ -873,11 +875,11 @@ export function buildCreateSlice<
   }
   for (const [name, creator] of Object.entries<
     ReducerCreator<RegisteredReducerType>
-  >(creators)) {
+  >(creatorMap)) {
     if (name === 'reducer' || name === 'preparedReducer') {
       throw new Error('Cannot use reserved creator name: ' + name)
     }
-    definers[name] = creator.define
+    creators[name] = creator.define
     handlers[creator.type] = creator.handle
   }
   return function createSlice<
@@ -957,7 +959,7 @@ export function buildCreateSlice<
     }
 
     if (typeof options.reducers === 'function') {
-      const reducers = options.reducers(definers as any)
+      const reducers = options.reducers(creators as any)
       for (const [reducerName, reducerDefinition] of Object.entries(reducers)) {
         const { _reducerDefinitionType: type } = reducerDefinition
         const handler = handlers[type as RegisteredReducerType]
diff --git a/packages/toolkit/src/tsHelpers.ts b/packages/toolkit/src/tsHelpers.ts
index fc6286402c..5155310d21 100644
--- a/packages/toolkit/src/tsHelpers.ts
+++ b/packages/toolkit/src/tsHelpers.ts
@@ -206,6 +206,6 @@ export type Tail<T extends any[]> = T extends [any, ...infer Tail]
   ? Tail
   : never
 
-export type KeysMatching<T, V> = {
+export type KeysForValueOfType<T, V> = {
   [K in keyof T]: T[K] extends V ? K : never
 }[keyof T]
diff --git a/packages/toolkit/tsconfig.base.json b/packages/toolkit/tsconfig.base.json
index 4c20a221f5..96c705b70f 100644
--- a/packages/toolkit/tsconfig.base.json
+++ b/packages/toolkit/tsconfig.base.json
@@ -39,33 +39,18 @@
       "vitest/globals"
     ],
     "paths": {
-      "@reduxjs/toolkit": [
-        "src/index.ts"
-      ], // @remap-prod-remove-line
-      "@reduxjs/toolkit/react": [
-        "src/react/index.ts"
-      ], // @remap-prod-remove-line
-      "@reduxjs/toolkit/query": [
-        "src/query/index.ts"
-      ], // @remap-prod-remove-line
-      "@reduxjs/toolkit/query/react": [
-        "src/query/react/index.ts"
-      ], // @remap-prod-remove-line
+      "@reduxjs/toolkit": ["src/index.ts"], // @remap-prod-remove-line
+      "@reduxjs/toolkit/react": ["src/react/index.ts"], // @remap-prod-remove-line
+      "@reduxjs/toolkit/query": ["src/query/index.ts"], // @remap-prod-remove-line
+      "@reduxjs/toolkit/query/react": ["src/query/react/index.ts"], // @remap-prod-remove-line
       // for type imports in tests only
-      "@reduxjs/toolkit/dist/*": [
-        "src/*"
-      ], // @remap-prod-remove-line
+      "@reduxjs/toolkit/dist/*": ["src/*"], // @remap-prod-remove-line
       // for type imports in tests only
-      "@reduxjs/toolkit/dist/query/*": [
-        "src/query/*"
-      ], // @remap-prod-remove-line
+      "@reduxjs/toolkit/dist/query/*": ["src/query/*"], // @remap-prod-remove-line
       // internal imports in tests only
-      "@internal/*": [
-        "src/*"
-      ],
-      "react": [
-        "../../node_modules/react"
-      ]
+      "@internal/*": ["src/*"],
+      // prevent resolving "react" to "@reduxjs/toolkit/react" entry point
+      "react": ["../../node_modules/react"]
     }
   }
 }
\ No newline at end of file

From cad9cb9747373b52a1332942cbc0daeb21be2931 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.j.durrant@gmail.com>
Date: Sat, 18 Nov 2023 22:53:53 +0000
Subject: [PATCH 003/178] fix docs

---
 docs/api/createSlice.mdx | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/docs/api/createSlice.mdx b/docs/api/createSlice.mdx
index d7ef6fec79..aa4e0f412c 100644
--- a/docs/api/createSlice.mdx
+++ b/docs/api/createSlice.mdx
@@ -141,7 +141,11 @@ Alternatively, the `reducers` field can be a callback which receives a "create"
 The main benefit of this is that you can create [async thunks](./createAsyncThunk) as part of your slice (though for bundle size reasons, you [need a bit of setup for this](#createasyncthunk)). Types are also slightly simplified for prepared reducers.
 
 ```ts title="Creator callback for reducers"
-import { createSlice, nanoid } from '@reduxjs/toolkit'
+import { buildCreateSlice, asyncThunkCreator, nanoid } from '@reduxjs/toolkit'
+
+const createSlice = buildCreateSlice({
+  creators: { asyncThunk: asyncThunkCreator },
+})
 
 interface Item {
   id: string

From cef24b7e7fce74cca50968b1c6b85773389a9083 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.j.durrant@gmail.com>
Date: Sat, 18 Nov 2023 22:59:59 +0000
Subject: [PATCH 004/178] remove unhappy line

---
 packages/toolkit/src/tests/createSlice.typetest.ts | 1 -
 1 file changed, 1 deletion(-)

diff --git a/packages/toolkit/src/tests/createSlice.typetest.ts b/packages/toolkit/src/tests/createSlice.typetest.ts
index 310464713a..79bce3f311 100644
--- a/packages/toolkit/src/tests/createSlice.typetest.ts
+++ b/packages/toolkit/src/tests/createSlice.typetest.ts
@@ -870,7 +870,6 @@ const value = actionCreators.anyKey
  * Test: buildCreateSlice
  */
 {
-  expectExactType(createSlice)(buildCreateSlice())
   buildCreateSlice({ creators: { asyncThunk: asyncThunkCreator } })
   // @ts-expect-error prevent passing reducer key
   buildCreateSlice({ creators: { reducer: asyncThunkCreator } })

From 1315a658c1c5aa785a6c34440365b04f9b307ae5 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.j.durrant@gmail.com>
Date: Sun, 19 Nov 2023 12:15:10 +0000
Subject: [PATCH 005/178] Add custom creator test

---
 packages/toolkit/src/createSlice.ts           |   4 +-
 packages/toolkit/src/index.ts                 |   5 +
 .../toolkit/src/tests/createSlice.test.ts     | 158 +++++++++++++++++-
 3 files changed, 163 insertions(+), 4 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 02e06356d8..c1e58aed29 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -43,7 +43,7 @@ export interface ReducerTypes extends Record<ReducerType, true> {}
 
 export type RegisteredReducerType = KeysForValueOfType<ReducerTypes, true>
 
-interface ReducerDefinition<
+export interface ReducerDefinition<
   T extends RegisteredReducerType = RegisteredReducerType
 > {
   _reducerDefinitionType: T
@@ -639,7 +639,7 @@ export type SliceSelectors<State> = {
   [K: string]: (sliceState: State, ...args: any[]) => any
 }
 
-type SliceActionType<
+export type SliceActionType<
   SliceName extends string,
   ActionName extends keyof any
 > = ActionName extends string | number ? `${SliceName}/${ActionName}` : string
diff --git a/packages/toolkit/src/index.ts b/packages/toolkit/src/index.ts
index b43d3e8777..e17d06f9b1 100644
--- a/packages/toolkit/src/index.ts
+++ b/packages/toolkit/src/index.ts
@@ -85,6 +85,11 @@ export type {
   CaseReducerWithPrepare,
   ReducerCreators,
   SliceSelectors,
+  ReducerTypes,
+  SliceReducerCreators,
+  ReducerDefinition,
+  ReducerCreator,
+  SliceActionType,
 } from './createSlice'
 export type { ActionCreatorInvariantMiddlewareOptions } from './actionCreatorInvariantMiddleware'
 export { createActionCreatorInvariantMiddleware } from './actionCreatorInvariantMiddleware'
diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index efcb0fcf8d..944df5c58a 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -1,5 +1,16 @@
 import { vi } from 'vitest'
-import type { PayloadAction, WithSlice } from '@reduxjs/toolkit'
+import type {
+  Action,
+  CaseReducer,
+  PayloadAction,
+  PayloadActionCreator,
+  ReducerCreator,
+  ReducerDefinition,
+  SliceActionType,
+  SliceCaseReducers,
+  ThunkAction,
+  WithSlice,
+} from '@reduxjs/toolkit'
 import {
   asyncThunkCreator,
   buildCreateSlice,
@@ -7,6 +18,7 @@ import {
   combineSlices,
   createSlice,
   createAction,
+  nanoid,
 } from '@reduxjs/toolkit'
 import {
   mockConsole,
@@ -14,6 +26,93 @@ import {
   getLog,
 } from 'console-testing-library/pure'
 
+interface LoaderReducerDefinition<State> extends ReducerDefinition<'loader'> {
+  started?: CaseReducer<State, PayloadAction<string>>
+  ended?: CaseReducer<State, PayloadAction<string>>
+}
+
+declare module '@reduxjs/toolkit' {
+  export interface ReducerTypes {
+    loader: true
+  }
+  export interface SliceReducerCreators<
+    State = any,
+    CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
+    Name extends string = string
+  > {
+    loader: {
+      create(
+        reducers: Pick<LoaderReducerDefinition<State>, 'ended' | 'started'>
+      ): LoaderReducerDefinition<State>
+      actions: {
+        [ReducerName in keyof CaseReducers as CaseReducers[ReducerName] extends LoaderReducerDefinition<State>
+          ? ReducerName
+          : never]: (() => ThunkAction<
+          { loaderId: string; end: () => void },
+          unknown,
+          unknown,
+          Action
+        >) & {
+          started: PayloadActionCreator<
+            string,
+            `${SliceActionType<Name, ReducerName>}/started`
+          >
+          ended: PayloadActionCreator<
+            string,
+            `${SliceActionType<Name, ReducerName>}/ended`
+          >
+        }
+      }
+      caseReducers: {
+        [ReducerName in keyof CaseReducers as CaseReducers[ReducerName] extends LoaderReducerDefinition<State>
+          ? ReducerName
+          : never]: Required<
+          Pick<LoaderReducerDefinition<State>, 'ended' | 'started'>
+        >
+      }
+    }
+  }
+}
+
+export const loaderCreator: ReducerCreator<'loader'> = {
+  type: 'loader',
+  define(reducers) {
+    return {
+      _reducerDefinitionType: 'loader',
+      ...reducers,
+    }
+  },
+  handle({ reducerName, type }, { started, ended }, context) {
+    const startedAction = createAction<string>(type + '/started')
+    const endedAction = createAction<string>(type + '/ended')
+
+    function thunkCreator(): ThunkAction<
+      { loaderId: string; end: () => void },
+      unknown,
+      unknown,
+      Action
+    > {
+      return (dispatch) => {
+        const loaderId = nanoid()
+        dispatch(startedAction(loaderId))
+        return {
+          loaderId,
+          end: () => {
+            dispatch(endedAction(loaderId))
+          },
+        }
+      }
+    }
+    Object.assign(thunkCreator, { started: startedAction, ended: endedAction })
+
+    if (started) context.addCase(startedAction, started)
+    if (ended) context.addCase(endedAction, ended)
+
+    context.exposeAction(reducerName, thunkCreator)
+    context.exposeCaseReducer(reducerName, { started, ended })
+  },
+}
+
 type CreateSlice = typeof createSlice
 
 describe('createSlice', () => {
@@ -826,7 +925,62 @@ describe('createSlice', () => {
             },
           }),
         })
-      ).toThrowErrorMatchingInlineSnapshot('"Unsupported reducer type: undefined"')
+      ).toThrowErrorMatchingInlineSnapshot(
+        '"Unsupported reducer type: undefined"'
+      )
+    })
+  })
+  describe('custom slice reducer creators', () => {
+    test('allows passing custom reducer creators, which can add actions and case reducers', () => {
+      const createLoaderSlice = buildCreateSlice({
+        creators: { loader: loaderCreator },
+      })
+
+      const loaderSlice = createLoaderSlice({
+        name: 'loader',
+        initialState: {} as Partial<Record<string, true>>,
+        reducers: (create) => ({
+          addLoader: create.loader({
+            started: (state, { payload }) => {
+              state[payload] = true
+            },
+            ended: (state, { payload }) => {
+              delete state[payload]
+            },
+          }),
+        }),
+        selectors: {
+          selectLoader: (state, id: string) => state[id],
+        },
+      })
+
+      const { addLoader } = loaderSlice.actions
+      const { selectLoader } = loaderSlice.selectors
+
+      expect(addLoader).toEqual(expect.any(Function))
+      expect(addLoader.started).toEqual(expect.any(Function))
+      expect(addLoader.started.type).toBe('loader/addLoader/started')
+
+      const store = configureStore({
+        reducer: {
+          [loaderSlice.reducerPath]: loaderSlice.reducer,
+          actions: (state: Action[] = [], action) => [...state, action],
+        },
+      })
+
+      expect(store.getState().loader).toEqual({})
+
+      const { loaderId, end } = store.dispatch(addLoader())
+      expect(selectLoader(store.getState(), loaderId)).toBe(true)
+
+      end()
+      expect(selectLoader(store.getState(), loaderId)).toBe(undefined)
+
+      // slice to ignore INIT action
+      expect(store.getState().actions.slice(1)).toEqual([
+        addLoader.started(loaderId),
+        addLoader.ended(loaderId),
+      ])
     })
   })
 })

From 204c927b37d0791c55e673968bc71fd1d68c3741 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.j.durrant@gmail.com>
Date: Sun, 19 Nov 2023 12:17:47 +0000
Subject: [PATCH 006/178] use matcher to avoid slice hack

---
 packages/toolkit/src/tests/createSlice.test.ts | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index 944df5c58a..1ea49b2f67 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -18,6 +18,7 @@ import {
   combineSlices,
   createSlice,
   createAction,
+  isAnyOf,
   nanoid,
 } from '@reduxjs/toolkit'
 import {
@@ -961,10 +962,13 @@ describe('createSlice', () => {
       expect(addLoader.started).toEqual(expect.any(Function))
       expect(addLoader.started.type).toBe('loader/addLoader/started')
 
+      const isLoaderAction = isAnyOf(addLoader.started, addLoader.ended)
+
       const store = configureStore({
         reducer: {
           [loaderSlice.reducerPath]: loaderSlice.reducer,
-          actions: (state: Action[] = [], action) => [...state, action],
+          actions: (state: PayloadAction<string>[] = [], action) =>
+            isLoaderAction(action) ? [...state, action] : state,
         },
       })
 
@@ -976,8 +980,7 @@ describe('createSlice', () => {
       end()
       expect(selectLoader(store.getState(), loaderId)).toBe(undefined)
 
-      // slice to ignore INIT action
-      expect(store.getState().actions.slice(1)).toEqual([
+      expect(store.getState().actions).toEqual([
         addLoader.started(loaderId),
         addLoader.ended(loaderId),
       ])

From 5074af3e235ad6f3b419ecb78ff46fb473bd3266 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.j.durrant@gmail.com>
Date: Sun, 19 Nov 2023 12:22:14 +0000
Subject: [PATCH 007/178] use selectSlice because we can

---
 packages/toolkit/src/tests/createSlice.test.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index 1ea49b2f67..80481f253c 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -972,7 +972,7 @@ describe('createSlice', () => {
         },
       })
 
-      expect(store.getState().loader).toEqual({})
+      expect(loaderSlice.selectSlice(store.getState())).toEqual({})
 
       const { loaderId, end } = store.dispatch(addLoader())
       expect(selectLoader(store.getState(), loaderId)).toBe(true)

From be096e98b7bd605aca3457f3ac7935f66f92c50a Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.j.durrant@gmail.com>
Date: Sun, 19 Nov 2023 12:48:13 +0000
Subject: [PATCH 008/178] add specific error message when reducer definition
 type is undefined

---
 packages/toolkit/src/createSlice.ts            | 5 +++++
 packages/toolkit/src/tests/createSlice.test.ts | 4 +---
 2 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index c1e58aed29..ce47d7fd32 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -962,6 +962,11 @@ export function buildCreateSlice<
       const reducers = options.reducers(creators as any)
       for (const [reducerName, reducerDefinition] of Object.entries(reducers)) {
         const { _reducerDefinitionType: type } = reducerDefinition
+        if (typeof type === 'undefined') {
+          throw new Error(
+            'Please use reducer creators passed to callback. Each reducer definition must have a `_reducerDefinitionType` property indicating which handler to use.'
+          )
+        }
         const handler = handlers[type as RegisteredReducerType]
         if (!handler) {
           throw new Error('Unsupported reducer type: ' + type)
diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index 80481f253c..d0f67d1062 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -926,9 +926,7 @@ describe('createSlice', () => {
             },
           }),
         })
-      ).toThrowErrorMatchingInlineSnapshot(
-        '"Unsupported reducer type: undefined"'
-      )
+      ).toThrowErrorMatchingInlineSnapshot('"Please use reducer creators passed to callback. Each reducer definition must have a `_reducerDefinitionType` property indicating which handler to use."')
     })
   })
   describe('custom slice reducer creators', () => {

From 346f2276ee31f432ef55cfc2baa99d68aefbdac9 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.j.durrant@gmail.com>
Date: Sat, 23 Dec 2023 11:28:15 +0000
Subject: [PATCH 009/178] remove underscore

---
 packages/toolkit/src/createSlice.ts | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index ce47d7fd32..b650eee5d2 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -30,7 +30,7 @@ import type {
   AsyncThunkPayloadCreator,
   OverrideThunkApiConfigs,
 } from './createAsyncThunk'
-import { createAsyncThunk as _createAsyncThunk } from './createAsyncThunk'
+import { createAsyncThunk as createAsyncThunk } from './createAsyncThunk'
 import { emplace } from './utils'
 
 export enum ReducerType {
@@ -816,7 +816,7 @@ export const asyncThunkCreator: ReducerCreator<ReducerType.asyncThunk> = {
   handle({ type, reducerName }, definition, context) {
     const { payloadCreator, fulfilled, pending, rejected, settled, options } =
       definition
-    const thunk = _createAsyncThunk(type, payloadCreator, options as any)
+    const thunk = createAsyncThunk(type, payloadCreator, options as any)
     context.exposeAction(reducerName, thunk)
 
     if (fulfilled) {

From 295eebc3c4447d728e8a3327dbe8bcd16c324f6c Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.j.durrant@gmail.com>
Date: Sat, 23 Dec 2023 13:37:36 +0000
Subject: [PATCH 010/178] update errors.json

---
 errors.json | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/errors.json b/errors.json
index ac6286a866..68aa8a4a7a 100644
--- a/errors.json
+++ b/errors.json
@@ -37,5 +37,8 @@
   "35": "Existing Redux context detected. If you already have a store set up, please use the traditional Redux setup.",
   "36": "When using custom hooks for context, all  hooks need to be provided: .\\nHook  was either not provided or not a function.",
   "37": "Warning: Middleware for RTK-Query API at reducerPath \"\" has not been added to the store.\n    You must add the middleware for RTK-Query to function correctly!",
-  "38": "Cannot refetch a query that has not been started yet."
-}
+  "38": "Cannot refetch a query that has not been started yet.",
+  "39": "Cannot use reserved creator name: name",
+  "40": "Please use reducer creators passed to callback. Each reducer definition must have a `_reducerDefinitionType` property indicating which handler to use.",
+  "41": "Unsupported reducer type: type"
+}
\ No newline at end of file

From 0e676b6192d11a6f329425e7e641f0a6255455ea Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.j.durrant@gmail.com>
Date: Wed, 27 Dec 2023 21:57:54 +0000
Subject: [PATCH 011/178] remove unnecessary rename

---
 packages/toolkit/src/createSlice.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index b650eee5d2..2b309340a1 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -30,7 +30,7 @@ import type {
   AsyncThunkPayloadCreator,
   OverrideThunkApiConfigs,
 } from './createAsyncThunk'
-import { createAsyncThunk as createAsyncThunk } from './createAsyncThunk'
+import { createAsyncThunk } from './createAsyncThunk'
 import { emplace } from './utils'
 
 export enum ReducerType {

From 3f502c2499ae758d149b3dded9198c7595b2da19 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.j.durrant@gmail.com>
Date: Wed, 27 Dec 2023 22:00:30 +0000
Subject: [PATCH 012/178] add condition creator test

---
 .../toolkit/src/tests/createSlice.test.ts     | 128 +++++++++++++++++-
 1 file changed, 127 insertions(+), 1 deletion(-)

diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index 8da14ffb85..e1f72b1cb9 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -1,6 +1,7 @@
 import { vi } from 'vitest'
 import type {
   Action,
+  AnyListenerPredicate,
   CaseReducer,
   PayloadAction,
   PayloadActionCreator,
@@ -9,6 +10,8 @@ import type {
   SliceActionType,
   SliceCaseReducers,
   ThunkAction,
+  UnknownAction,
+  UnsubscribeListener,
   WithSlice,
 } from '@reduxjs/toolkit'
 import {
@@ -20,6 +23,8 @@ import {
   createAction,
   isAnyOf,
   nanoid,
+  addListener,
+  createListenerMiddleware,
 } from '@reduxjs/toolkit'
 import {
   mockConsole,
@@ -35,6 +40,7 @@ interface LoaderReducerDefinition<State> extends ReducerDefinition<'loader'> {
 declare module '@reduxjs/toolkit' {
   export interface ReducerTypes {
     loader: true
+    condition: true
   }
   export interface SliceReducerCreators<
     State = any,
@@ -72,6 +78,31 @@ declare module '@reduxjs/toolkit' {
         >
       }
     }
+    condition: {
+      create<Args extends any[]>(
+        makePredicate: (...args: Args) => AnyListenerPredicate<unknown>
+      ): ReducerDefinition<'condition'> & {
+        makePredicate: (...args: Args) => AnyListenerPredicate<unknown>
+      }
+      actions: {
+        [ReducerName in keyof CaseReducers as CaseReducers[ReducerName] extends ReducerDefinition<'condition'>
+          ? ReducerName
+          : never]: CaseReducers[ReducerName] extends {
+          makePredicate: (...args: infer Args) => AnyListenerPredicate<unknown>
+        }
+          ? (
+              timeout?: number,
+              ...args: Args
+            ) => ThunkAction<
+              Promise<boolean> & { unsubscribe?: UnsubscribeListener },
+              unknown,
+              unknown,
+              UnknownAction
+            >
+          : never
+      }
+      caseReducers: {}
+    }
   }
 }
 
@@ -114,6 +145,52 @@ export const loaderCreator: ReducerCreator<'loader'> = {
   },
 }
 
+export const conditionCreator: ReducerCreator<'condition'> = {
+  type: 'condition',
+  define(makePredicate) {
+    return { _reducerDefinitionType: 'condition', makePredicate }
+  },
+  handle({ reducerName, type }, { makePredicate }, context) {
+    const trigger = createAction(type, (id: string, args: unknown[]) => ({
+      payload: id,
+      meta: { args },
+    }))
+    function thunkCreator(
+      timeout?: number,
+      ...args: any[]
+    ): ThunkAction<
+      Promise<boolean> & { unsubscribe?: UnsubscribeListener },
+      unknown,
+      unknown,
+      UnknownAction
+    > {
+      const predicate = makePredicate(...args)
+      return (dispatch) => {
+        const listenerId = nanoid()
+        let unsubscribe: UnsubscribeListener | undefined = undefined
+        const promise = new Promise<boolean>((resolve, reject) => {
+          unsubscribe = dispatch(
+            addListener({
+              predicate: (action) =>
+                trigger.match(action) && action.payload === listenerId,
+              effect: (_, { condition, unsubscribe, signal }) => {
+                signal.addEventListener('abort', () => reject(false))
+                return condition(predicate, timeout)
+                  .then(resolve)
+                  .catch(reject)
+                  .finally(unsubscribe)
+              },
+            })
+          ) as any
+          dispatch(trigger(listenerId, args))
+        })
+        return Object.assign(promise, { unsubscribe })
+      }
+    }
+    context.exposeAction(reducerName, thunkCreator)
+  },
+}
+
 type CreateSlice = typeof createSlice
 
 describe('createSlice', () => {
@@ -926,7 +1003,9 @@ describe('createSlice', () => {
             },
           }),
         })
-      ).toThrowErrorMatchingInlineSnapshot('"Please use reducer creators passed to callback. Each reducer definition must have a `_reducerDefinitionType` property indicating which handler to use."')
+      ).toThrowErrorMatchingInlineSnapshot(
+        '"Please use reducer creators passed to callback. Each reducer definition must have a `_reducerDefinitionType` property indicating which handler to use."'
+      )
     })
   })
   describe('custom slice reducer creators', () => {
@@ -983,5 +1062,52 @@ describe('createSlice', () => {
         addLoader.ended(loaderId),
       ])
     })
+    test('condition creator', async () => {
+      const createConditionSlice = buildCreateSlice({
+        creators: { condition: conditionCreator },
+      })
+
+      const counterSlice = createConditionSlice({
+        name: 'counter',
+        initialState: { value: 0 },
+        reducers: (create) => ({
+          increment: create.reducer((state) => void state.value++),
+          waitUntilValue: create.condition(
+            (value: number) =>
+              (_action, state): boolean =>
+                counterSlice.selectors.selectValue(state as any) === value
+          ),
+        }),
+        selectors: {
+          selectValue: (state) => state.value,
+        },
+      })
+
+      const {
+        actions: { increment, waitUntilValue },
+        selectors: { selectValue },
+      } = counterSlice
+
+      const listener = createListenerMiddleware()
+
+      const reducer = combineSlices(counterSlice)
+
+      const store = configureStore({
+        reducer,
+        middleware: (gDM) => gDM().concat(listener.middleware),
+      })
+
+      const promise = store.dispatch(waitUntilValue(undefined, 5))
+      expect(promise).toEqual(expect.any(Promise))
+      expect(promise.unsubscribe).toEqual(expect.any(Function))
+
+      for (let i = 1; i <= 4; i++) {
+        store.dispatch(increment())
+        expect(selectValue(store.getState())).toBe(i)
+      }
+      store.dispatch(increment())
+      expect(selectValue(store.getState())).toBe(5)
+      expect(await promise).toBe(true)
+    })
   })
 })

From e21a7da36f65154df3be2c5959cdeadae7872da2 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.j.durrant@gmail.com>
Date: Wed, 27 Dec 2023 22:20:42 +0000
Subject: [PATCH 013/178] check promise status synchronously

---
 packages/toolkit/src/tests/createSlice.test.ts | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index e1f72b1cb9..cf7d868785 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -1062,6 +1062,18 @@ describe('createSlice', () => {
         addLoader.ended(loaderId),
       ])
     })
+
+    function withStatus<P extends PromiseLike<any>>(promiseLike: P) {
+      const assigned = Object.assign(promiseLike, {
+        status: 'pending' as 'pending' | 'fulfilled' | 'rejected',
+      })
+      promiseLike.then(
+        () => (assigned.status = 'fulfilled'),
+        () => (assigned.status = 'rejected')
+      )
+      return assigned
+    }
+
     test('condition creator', async () => {
       const createConditionSlice = buildCreateSlice({
         creators: { condition: conditionCreator },
@@ -1097,17 +1109,19 @@ describe('createSlice', () => {
         middleware: (gDM) => gDM().concat(listener.middleware),
       })
 
-      const promise = store.dispatch(waitUntilValue(undefined, 5))
+      const promise = withStatus(store.dispatch(waitUntilValue(undefined, 5)))
       expect(promise).toEqual(expect.any(Promise))
       expect(promise.unsubscribe).toEqual(expect.any(Function))
 
       for (let i = 1; i <= 4; i++) {
         store.dispatch(increment())
         expect(selectValue(store.getState())).toBe(i)
+        expect(promise.status).toBe('pending')
       }
       store.dispatch(increment())
       expect(selectValue(store.getState())).toBe(5)
       expect(await promise).toBe(true)
+      expect(promise.status).toBe('fulfilled')
     })
   })
 })

From f725ab60142fd9bc70b8ecf104ef4f5ada1ba754 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.j.durrant@gmail.com>
Date: Wed, 27 Dec 2023 22:23:24 +0000
Subject: [PATCH 014/178] use resolves chain

---
 packages/toolkit/src/tests/createSlice.test.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index cf7d868785..769f8435b9 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -1120,7 +1120,7 @@ describe('createSlice', () => {
       }
       store.dispatch(increment())
       expect(selectValue(store.getState())).toBe(5)
-      expect(await promise).toBe(true)
+      await expect(promise).resolves.toBe(true)
       expect(promise.status).toBe('fulfilled')
     })
   })

From 640b9c52af12085ef0e76b1392cba38dfe77a118 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.j.durrant@gmail.com>
Date: Wed, 27 Dec 2023 22:33:13 +0000
Subject: [PATCH 015/178] shuffle creator locations

---
 .../toolkit/src/tests/createSlice.test.ts     | 320 +++++++++---------
 1 file changed, 161 insertions(+), 159 deletions(-)

diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index 769f8435b9..97f92ab2a7 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -32,165 +32,6 @@ import {
   getLog,
 } from 'console-testing-library/pure'
 
-interface LoaderReducerDefinition<State> extends ReducerDefinition<'loader'> {
-  started?: CaseReducer<State, PayloadAction<string>>
-  ended?: CaseReducer<State, PayloadAction<string>>
-}
-
-declare module '@reduxjs/toolkit' {
-  export interface ReducerTypes {
-    loader: true
-    condition: true
-  }
-  export interface SliceReducerCreators<
-    State = any,
-    CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
-    Name extends string = string
-  > {
-    loader: {
-      create(
-        reducers: Pick<LoaderReducerDefinition<State>, 'ended' | 'started'>
-      ): LoaderReducerDefinition<State>
-      actions: {
-        [ReducerName in keyof CaseReducers as CaseReducers[ReducerName] extends LoaderReducerDefinition<State>
-          ? ReducerName
-          : never]: (() => ThunkAction<
-          { loaderId: string; end: () => void },
-          unknown,
-          unknown,
-          Action
-        >) & {
-          started: PayloadActionCreator<
-            string,
-            `${SliceActionType<Name, ReducerName>}/started`
-          >
-          ended: PayloadActionCreator<
-            string,
-            `${SliceActionType<Name, ReducerName>}/ended`
-          >
-        }
-      }
-      caseReducers: {
-        [ReducerName in keyof CaseReducers as CaseReducers[ReducerName] extends LoaderReducerDefinition<State>
-          ? ReducerName
-          : never]: Required<
-          Pick<LoaderReducerDefinition<State>, 'ended' | 'started'>
-        >
-      }
-    }
-    condition: {
-      create<Args extends any[]>(
-        makePredicate: (...args: Args) => AnyListenerPredicate<unknown>
-      ): ReducerDefinition<'condition'> & {
-        makePredicate: (...args: Args) => AnyListenerPredicate<unknown>
-      }
-      actions: {
-        [ReducerName in keyof CaseReducers as CaseReducers[ReducerName] extends ReducerDefinition<'condition'>
-          ? ReducerName
-          : never]: CaseReducers[ReducerName] extends {
-          makePredicate: (...args: infer Args) => AnyListenerPredicate<unknown>
-        }
-          ? (
-              timeout?: number,
-              ...args: Args
-            ) => ThunkAction<
-              Promise<boolean> & { unsubscribe?: UnsubscribeListener },
-              unknown,
-              unknown,
-              UnknownAction
-            >
-          : never
-      }
-      caseReducers: {}
-    }
-  }
-}
-
-export const loaderCreator: ReducerCreator<'loader'> = {
-  type: 'loader',
-  define(reducers) {
-    return {
-      _reducerDefinitionType: 'loader',
-      ...reducers,
-    }
-  },
-  handle({ reducerName, type }, { started, ended }, context) {
-    const startedAction = createAction<string>(type + '/started')
-    const endedAction = createAction<string>(type + '/ended')
-
-    function thunkCreator(): ThunkAction<
-      { loaderId: string; end: () => void },
-      unknown,
-      unknown,
-      Action
-    > {
-      return (dispatch) => {
-        const loaderId = nanoid()
-        dispatch(startedAction(loaderId))
-        return {
-          loaderId,
-          end: () => {
-            dispatch(endedAction(loaderId))
-          },
-        }
-      }
-    }
-    Object.assign(thunkCreator, { started: startedAction, ended: endedAction })
-
-    if (started) context.addCase(startedAction, started)
-    if (ended) context.addCase(endedAction, ended)
-
-    context.exposeAction(reducerName, thunkCreator)
-    context.exposeCaseReducer(reducerName, { started, ended })
-  },
-}
-
-export const conditionCreator: ReducerCreator<'condition'> = {
-  type: 'condition',
-  define(makePredicate) {
-    return { _reducerDefinitionType: 'condition', makePredicate }
-  },
-  handle({ reducerName, type }, { makePredicate }, context) {
-    const trigger = createAction(type, (id: string, args: unknown[]) => ({
-      payload: id,
-      meta: { args },
-    }))
-    function thunkCreator(
-      timeout?: number,
-      ...args: any[]
-    ): ThunkAction<
-      Promise<boolean> & { unsubscribe?: UnsubscribeListener },
-      unknown,
-      unknown,
-      UnknownAction
-    > {
-      const predicate = makePredicate(...args)
-      return (dispatch) => {
-        const listenerId = nanoid()
-        let unsubscribe: UnsubscribeListener | undefined = undefined
-        const promise = new Promise<boolean>((resolve, reject) => {
-          unsubscribe = dispatch(
-            addListener({
-              predicate: (action) =>
-                trigger.match(action) && action.payload === listenerId,
-              effect: (_, { condition, unsubscribe, signal }) => {
-                signal.addEventListener('abort', () => reject(false))
-                return condition(predicate, timeout)
-                  .then(resolve)
-                  .catch(reject)
-                  .finally(unsubscribe)
-              },
-            })
-          ) as any
-          dispatch(trigger(listenerId, args))
-        })
-        return Object.assign(promise, { unsubscribe })
-      }
-    }
-    context.exposeAction(reducerName, thunkCreator)
-  },
-}
-
 type CreateSlice = typeof createSlice
 
 describe('createSlice', () => {
@@ -1009,6 +850,93 @@ describe('createSlice', () => {
     })
   })
   describe('custom slice reducer creators', () => {
+    const loaderCreator: ReducerCreator<'loader'> = {
+      type: 'loader',
+      define(reducers) {
+        return {
+          _reducerDefinitionType: 'loader',
+          ...reducers,
+        }
+      },
+      handle({ reducerName, type }, { started, ended }, context) {
+        const startedAction = createAction<string>(type + '/started')
+        const endedAction = createAction<string>(type + '/ended')
+
+        function thunkCreator(): ThunkAction<
+          { loaderId: string; end: () => void },
+          unknown,
+          unknown,
+          Action
+        > {
+          return (dispatch) => {
+            const loaderId = nanoid()
+            dispatch(startedAction(loaderId))
+            return {
+              loaderId,
+              end: () => {
+                dispatch(endedAction(loaderId))
+              },
+            }
+          }
+        }
+        Object.assign(thunkCreator, {
+          started: startedAction,
+          ended: endedAction,
+        })
+
+        if (started) context.addCase(startedAction, started)
+        if (ended) context.addCase(endedAction, ended)
+
+        context.exposeAction(reducerName, thunkCreator)
+        context.exposeCaseReducer(reducerName, { started, ended })
+      },
+    }
+
+    const conditionCreator: ReducerCreator<'condition'> = {
+      type: 'condition',
+      define(makePredicate) {
+        return { _reducerDefinitionType: 'condition', makePredicate }
+      },
+      handle({ reducerName, type }, { makePredicate }, context) {
+        const trigger = createAction(type, (id: string, args: unknown[]) => ({
+          payload: id,
+          meta: { args },
+        }))
+        function thunkCreator(
+          timeout?: number,
+          ...args: any[]
+        ): ThunkAction<
+          Promise<boolean> & { unsubscribe?: UnsubscribeListener },
+          unknown,
+          unknown,
+          UnknownAction
+        > {
+          const predicate = makePredicate(...args)
+          return (dispatch) => {
+            const listenerId = nanoid()
+            let unsubscribe: UnsubscribeListener | undefined = undefined
+            const promise = new Promise<boolean>((resolve, reject) => {
+              unsubscribe = dispatch(
+                addListener({
+                  predicate: (action) =>
+                    trigger.match(action) && action.payload === listenerId,
+                  effect: (_, { condition, unsubscribe, signal }) => {
+                    signal.addEventListener('abort', () => reject(false))
+                    return condition(predicate, timeout)
+                      .then(resolve)
+                      .catch(reject)
+                      .finally(unsubscribe)
+                  },
+                })
+              ) as any
+              dispatch(trigger(listenerId, args))
+            })
+            return Object.assign(promise, { unsubscribe })
+          }
+        }
+        context.exposeAction(reducerName, thunkCreator)
+      },
+    }
     test('allows passing custom reducer creators, which can add actions and case reducers', () => {
       const createLoaderSlice = buildCreateSlice({
         creators: { loader: loaderCreator },
@@ -1125,3 +1053,77 @@ describe('createSlice', () => {
     })
   })
 })
+
+interface LoaderReducerDefinition<State> extends ReducerDefinition<'loader'> {
+  started?: CaseReducer<State, PayloadAction<string>>
+  ended?: CaseReducer<State, PayloadAction<string>>
+}
+
+declare module '@reduxjs/toolkit' {
+  export interface ReducerTypes {
+    loader: true
+    condition: true
+  }
+  export interface SliceReducerCreators<
+    State = any,
+    CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
+    Name extends string = string
+  > {
+    loader: {
+      create(
+        reducers: Pick<LoaderReducerDefinition<State>, 'ended' | 'started'>
+      ): LoaderReducerDefinition<State>
+      actions: {
+        [ReducerName in keyof CaseReducers as CaseReducers[ReducerName] extends LoaderReducerDefinition<State>
+          ? ReducerName
+          : never]: (() => ThunkAction<
+          { loaderId: string; end: () => void },
+          unknown,
+          unknown,
+          Action
+        >) & {
+          started: PayloadActionCreator<
+            string,
+            `${SliceActionType<Name, ReducerName>}/started`
+          >
+          ended: PayloadActionCreator<
+            string,
+            `${SliceActionType<Name, ReducerName>}/ended`
+          >
+        }
+      }
+      caseReducers: {
+        [ReducerName in keyof CaseReducers as CaseReducers[ReducerName] extends LoaderReducerDefinition<State>
+          ? ReducerName
+          : never]: Required<
+          Pick<LoaderReducerDefinition<State>, 'ended' | 'started'>
+        >
+      }
+    }
+    condition: {
+      create<Args extends any[]>(
+        makePredicate: (...args: Args) => AnyListenerPredicate<unknown>
+      ): ReducerDefinition<'condition'> & {
+        makePredicate: (...args: Args) => AnyListenerPredicate<unknown>
+      }
+      actions: {
+        [ReducerName in keyof CaseReducers as CaseReducers[ReducerName] extends ReducerDefinition<'condition'>
+          ? ReducerName
+          : never]: CaseReducers[ReducerName] extends {
+          makePredicate: (...args: infer Args) => AnyListenerPredicate<unknown>
+        }
+          ? (
+              timeout?: number,
+              ...args: Args
+            ) => ThunkAction<
+              Promise<boolean> & { unsubscribe?: UnsubscribeListener },
+              unknown,
+              unknown,
+              UnknownAction
+            >
+          : never
+      }
+      caseReducers: {}
+    }
+  }
+}

From 0d7669da8088152e8da704d16131f0991aeca501 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.j.durrant@gmail.com>
Date: Sat, 30 Dec 2023 00:59:37 +0000
Subject: [PATCH 016/178] add multireducer creator example

---
 .../toolkit/src/tests/createSlice.test.ts     | 73 +++++++++++++++++++
 1 file changed, 73 insertions(+)

diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index 97f92ab2a7..fb60e43209 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -6,6 +6,7 @@ import type {
   PayloadAction,
   PayloadActionCreator,
   ReducerCreator,
+  ReducerCreators,
   ReducerDefinition,
   SliceActionType,
   SliceCaseReducers,
@@ -31,6 +32,7 @@ import {
   createConsole,
   getLog,
 } from 'console-testing-library/pure'
+import type { CaseReducerDefinition } from '../createSlice'
 
 type CreateSlice = typeof createSlice
 
@@ -937,6 +939,23 @@ describe('createSlice', () => {
         context.exposeAction(reducerName, thunkCreator)
       },
     }
+    const fetchCreator: ReducerCreator<'fetch'> = {
+      type: 'fetch',
+      define() {
+        return {
+          start: this.reducer((state) => {
+            state.status = 'loading'
+          }),
+          success: this.reducer<any>((state, action) => {
+            state.data = action.payload
+            state.status = 'finished'
+          }),
+        }
+      },
+      handle() {
+        throw new Error("Shouldn't ever happen")
+      },
+    }
     test('allows passing custom reducer creators, which can add actions and case reducers', () => {
       const createLoaderSlice = buildCreateSlice({
         creators: { loader: loaderCreator },
@@ -1051,6 +1070,42 @@ describe('createSlice', () => {
       await expect(promise).resolves.toBe(true)
       expect(promise.status).toBe('fulfilled')
     })
+    test('creators can return multiple definitions to be spread', () => {
+      const createFetchSlice = buildCreateSlice({
+        creators: { fetchReducers: fetchCreator },
+      })
+
+      const fetchSlice = createFetchSlice({
+        name: 'fetch',
+        initialState: { status: 'loading' } as FetchState<string>,
+        reducers: (create) => ({
+          ...create.fetchReducers(),
+          magic: create.reducer((state) => {
+            state.status = 'finished'
+            state.data = 'hocus pocus'
+          }),
+        }),
+      })
+
+      const { start, success, magic } = fetchSlice.actions
+
+      expect(start).toEqual(expect.any(Function))
+      expect(start.type).toBe('fetch/start')
+    })
+    test.skip('creators can discourage their use if state is incompatible (types only)', () => {
+      const createFetchSlice = buildCreateSlice({
+        creators: { fetchReducers: fetchCreator },
+      })
+
+      const counterSlice = createFetchSlice({
+        name: 'counter',
+        initialState: { value: 0 },
+        reducers: (create) => ({
+          // @ts-expect-error
+          ...create.fetchReducers(),
+        }),
+      })
+    })
   })
 })
 
@@ -1059,10 +1114,16 @@ interface LoaderReducerDefinition<State> extends ReducerDefinition<'loader'> {
   ended?: CaseReducer<State, PayloadAction<string>>
 }
 
+interface FetchState<T> {
+  data?: T
+  status: 'loading' | 'finished' | 'error'
+}
+
 declare module '@reduxjs/toolkit' {
   export interface ReducerTypes {
     loader: true
     condition: true
+    fetch: true
   }
   export interface SliceReducerCreators<
     State = any,
@@ -1125,5 +1186,17 @@ declare module '@reduxjs/toolkit' {
       }
       caseReducers: {}
     }
+    fetch: {
+      create(this: ReducerCreators<State, {}>): State extends FetchState<
+        infer Data
+      >
+        ? {
+            start: CaseReducerDefinition<State, PayloadAction>
+            success: CaseReducerDefinition<State, PayloadAction<Data>>
+          }
+        : never
+      actions: {}
+      caseReducers: {}
+    }
   }
 }

From 604799a51c183a1cb6567edc78631f8f48d8b115 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.j.durrant@gmail.com>
Date: Sat, 30 Dec 2023 01:42:55 +0000
Subject: [PATCH 017/178] use symbols for reducer types, for clarity

---
 .../toolkit/src/tests/createSlice.test.ts     | 41 +++++++++++--------
 1 file changed, 24 insertions(+), 17 deletions(-)

diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index fb60e43209..b183d7c8b0 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -36,6 +36,10 @@ import type { CaseReducerDefinition } from '../createSlice'
 
 type CreateSlice = typeof createSlice
 
+const loaderCreatorType = Symbol()
+const conditionCreatorType = Symbol()
+const fetchCreatorType = Symbol()
+
 describe('createSlice', () => {
   let restore: () => void
 
@@ -852,11 +856,11 @@ describe('createSlice', () => {
     })
   })
   describe('custom slice reducer creators', () => {
-    const loaderCreator: ReducerCreator<'loader'> = {
-      type: 'loader',
+    const loaderCreator: ReducerCreator<typeof loaderCreatorType> = {
+      type: loaderCreatorType,
       define(reducers) {
         return {
-          _reducerDefinitionType: 'loader',
+          _reducerDefinitionType: loaderCreatorType,
           ...reducers,
         }
       },
@@ -894,10 +898,10 @@ describe('createSlice', () => {
       },
     }
 
-    const conditionCreator: ReducerCreator<'condition'> = {
-      type: 'condition',
+    const conditionCreator: ReducerCreator<typeof conditionCreatorType> = {
+      type: conditionCreatorType,
       define(makePredicate) {
-        return { _reducerDefinitionType: 'condition', makePredicate }
+        return { _reducerDefinitionType: conditionCreatorType, makePredicate }
       },
       handle({ reducerName, type }, { makePredicate }, context) {
         const trigger = createAction(type, (id: string, args: unknown[]) => ({
@@ -939,8 +943,8 @@ describe('createSlice', () => {
         context.exposeAction(reducerName, thunkCreator)
       },
     }
-    const fetchCreator: ReducerCreator<'fetch'> = {
-      type: 'fetch',
+    const fetchCreator: ReducerCreator<typeof fetchCreatorType> = {
+      type: fetchCreatorType,
       define() {
         return {
           start: this.reducer((state) => {
@@ -1109,7 +1113,8 @@ describe('createSlice', () => {
   })
 })
 
-interface LoaderReducerDefinition<State> extends ReducerDefinition<'loader'> {
+interface LoaderReducerDefinition<State>
+  extends ReducerDefinition<typeof loaderCreatorType> {
   started?: CaseReducer<State, PayloadAction<string>>
   ended?: CaseReducer<State, PayloadAction<string>>
 }
@@ -1121,16 +1126,16 @@ interface FetchState<T> {
 
 declare module '@reduxjs/toolkit' {
   export interface ReducerTypes {
-    loader: true
-    condition: true
-    fetch: true
+    [loaderCreatorType]: true
+    [conditionCreatorType]: true
+    [fetchCreatorType]: true
   }
   export interface SliceReducerCreators<
     State = any,
     CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
     Name extends string = string
   > {
-    loader: {
+    [loaderCreatorType]: {
       create(
         reducers: Pick<LoaderReducerDefinition<State>, 'ended' | 'started'>
       ): LoaderReducerDefinition<State>
@@ -1161,14 +1166,16 @@ declare module '@reduxjs/toolkit' {
         >
       }
     }
-    condition: {
+    [conditionCreatorType]: {
       create<Args extends any[]>(
         makePredicate: (...args: Args) => AnyListenerPredicate<unknown>
-      ): ReducerDefinition<'condition'> & {
+      ): ReducerDefinition<typeof conditionCreatorType> & {
         makePredicate: (...args: Args) => AnyListenerPredicate<unknown>
       }
       actions: {
-        [ReducerName in keyof CaseReducers as CaseReducers[ReducerName] extends ReducerDefinition<'condition'>
+        [ReducerName in keyof CaseReducers as CaseReducers[ReducerName] extends ReducerDefinition<
+          typeof conditionCreatorType
+        >
           ? ReducerName
           : never]: CaseReducers[ReducerName] extends {
           makePredicate: (...args: infer Args) => AnyListenerPredicate<unknown>
@@ -1186,7 +1193,7 @@ declare module '@reduxjs/toolkit' {
       }
       caseReducers: {}
     }
-    fetch: {
+    [fetchCreatorType]: {
       create(this: ReducerCreators<State, {}>): State extends FetchState<
         infer Data
       >

From 888e886aba5bf12810b0fa2a8793e8340172a869 Mon Sep 17 00:00:00 2001
From: Arya Emami <aryaemami59@yahoo.com>
Date: Fri, 29 Dec 2023 22:00:48 -0600
Subject: [PATCH 018/178] Modify `BuildCreateSliceConfig` to allow passing
 `asyncThunk` by default and prevent passing of `reducer` and
 `reducerWithPrepare`

  - Changed `BuildCreateSliceConfig` to allow passing `asyncThunk` without sacrificing autocompletion.
  - Implemented type constraints to disallow usage of `reducer` and `reducerWithPrepare`, removing them from autocomplete suggestions.
---
 packages/toolkit/src/createSlice.ts | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 2b309340a1..1e09ab0d5b 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -847,15 +847,18 @@ interface BuildCreateSliceConfig<
   CreatorMap extends Record<string, RegisteredReducerType>
 > {
   creators?: {
-    [Name in keyof CreatorMap]: ReducerCreator<CreatorMap[Name]>
-  } &
-    Partial<Record<'reducer' | 'preparedReducer', never>>
+    [Name in keyof CreatorMap]: Name extends 'reducer' | 'reducerWithPrepare'
+      ? never
+      : ReducerCreator<CreatorMap[Name]>
+  }
 }
 
 export function buildCreateSlice<
-  CreatorMap extends Record<string, RegisteredReducerType> = {}
+  CreatorMap extends Record<string, RegisteredReducerType> = Partial<
+    Record<'asyncThunk', RegisteredReducerType>
+  >
 >({
-  creators: creatorMap = {} as any,
+  creators: creatorMap = {} as Required<BuildCreateSliceConfig<CreatorMap>['creators']>,
 }: BuildCreateSliceConfig<CreatorMap> = {}) {
   const creators: Record<
     string,
@@ -875,7 +878,7 @@ export function buildCreateSlice<
   }
   for (const [name, creator] of Object.entries<
     ReducerCreator<RegisteredReducerType>
-  >(creatorMap)) {
+  >(creatorMap!)) {
     if (name === 'reducer' || name === 'preparedReducer') {
       throw new Error('Cannot use reserved creator name: ' + name)
     }

From 5cc55092b4d5ed0a3a6400df575593adb8776f96 Mon Sep 17 00:00:00 2001
From: Arya Emami <aryaemami59@yahoo.com>
Date: Fri, 29 Dec 2023 22:12:38 -0600
Subject: [PATCH 019/178] Fix typo from `reducerWithPrepare` ->
 `preparedReducer`

---
 packages/toolkit/src/createSlice.ts | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 1e09ab0d5b..3e4ed8af79 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -847,7 +847,7 @@ interface BuildCreateSliceConfig<
   CreatorMap extends Record<string, RegisteredReducerType>
 > {
   creators?: {
-    [Name in keyof CreatorMap]: Name extends 'reducer' | 'reducerWithPrepare'
+    [Name in keyof CreatorMap]: Name extends 'reducer' | 'preparedReducer'
       ? never
       : ReducerCreator<CreatorMap[Name]>
   }
@@ -858,7 +858,9 @@ export function buildCreateSlice<
     Record<'asyncThunk', RegisteredReducerType>
   >
 >({
-  creators: creatorMap = {} as Required<BuildCreateSliceConfig<CreatorMap>['creators']>,
+  creators: creatorMap = {} as Required<
+    BuildCreateSliceConfig<CreatorMap>['creators']
+  >,
 }: BuildCreateSliceConfig<CreatorMap> = {}) {
   const creators: Record<
     string,

From 0d60f506ee45da80700c44cdb4936042cc8b68ec Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.j.durrant@gmail.com>
Date: Sat, 30 Dec 2023 12:04:11 +0000
Subject: [PATCH 020/178] expose getInitialState to reducer creators

---
 packages/toolkit/src/createReducer.ts | 23 +++++++++++++++--------
 packages/toolkit/src/createSlice.ts   | 16 ++++++++++------
 2 files changed, 25 insertions(+), 14 deletions(-)

diff --git a/packages/toolkit/src/createReducer.ts b/packages/toolkit/src/createReducer.ts
index f0f2ac50be..2d1bf61b4a 100644
--- a/packages/toolkit/src/createReducer.ts
+++ b/packages/toolkit/src/createReducer.ts
@@ -152,14 +152,7 @@ export function createReducer<S extends NotFunction<any>>(
   let [actionsMap, finalActionMatchers, finalDefaultCaseReducer] =
     executeReducerBuilderCallback(mapOrBuilderCallback)
 
-  // Ensure the initial state gets frozen either way (if draftable)
-  let getInitialState: () => S
-  if (isStateFunction(initialState)) {
-    getInitialState = () => freezeDraftable(initialState())
-  } else {
-    const frozenInitialState = freezeDraftable(initialState)
-    getInitialState = () => frozenInitialState
-  }
+  const getInitialState = makeGetInitialState(initialState)
 
   function reducer(state = getInitialState(), action: any): S {
     let caseReducers = [
@@ -219,3 +212,17 @@ export function createReducer<S extends NotFunction<any>>(
 
   return reducer as ReducerWithInitialState<S>
 }
+
+export function makeGetInitialState<S extends NotFunction<any>>(
+  initialState: S | (() => S)
+) {
+  // Ensure the initial state gets frozen either way (if draftable)
+  let getInitialState: () => S
+  if (isStateFunction(initialState)) {
+    getInitialState = () => freezeDraftable(initialState())
+  } else {
+    const frozenInitialState = freezeDraftable(initialState)
+    getInitialState = () => frozenInitialState
+  }
+  return getInitialState
+}
diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 2b309340a1..236a32509c 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -13,7 +13,7 @@ import type {
   CaseReducer,
   ReducerWithInitialState,
 } from './createReducer'
-import { createReducer } from './createReducer'
+import { createReducer, makeGetInitialState } from './createReducer'
 import type { ActionReducerMapBuilder, TypedActionCreator } from './mapBuilders'
 import { executeReducerBuilderCallback } from './mapBuilders'
 import type {
@@ -252,6 +252,11 @@ interface ReducerHandlingContextMethods<
     // TODO: see if there's a way to get the actual type cleanly
     reducer: unknown
   ): ReducerHandlingContextMethods<State, ReducerType>
+  /**
+   * Provides access to the initial state value given to the slice.
+   * If a lazy state initializer was provided, it will be called and a fresh value returned.
+   */
+  getInitialState(): State
 }
 
 interface ReducerDetails {
@@ -914,6 +919,8 @@ export function buildCreateSlice<
       }
     }
 
+    const getInitialState = makeGetInitialState(options.initialState)
+
     const context: ReducerHandlingContext<State> = {
       sliceCaseReducersByName: {},
       sliceCaseReducersByType: {},
@@ -956,6 +963,7 @@ export function buildCreateSlice<
         context.sliceCaseReducersByName[name] = reducer
         return contextMethods
       },
+      getInitialState,
     }
 
     if (typeof options.reducers === 'function') {
@@ -1061,11 +1069,7 @@ export function buildCreateSlice<
       },
       actions: context.actionCreators as any,
       caseReducers: context.sliceCaseReducersByName as any,
-      getInitialState() {
-        if (!_reducer) _reducer = buildReducer()
-
-        return _reducer.getInitialState()
-      },
+      getInitialState,
       getSelectors(selectState: (rootState: any) => State = selectSelf) {
         const selectorCache = emplace(injectedSelectorCache, this, {
           insert: () => new WeakMap(),

From a993d910b964e995f38d50923f02e01ce3985689 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.j.durrant@gmail.com>
Date: Sat, 30 Dec 2023 12:12:37 +0000
Subject: [PATCH 021/178] tweak creators type to prevent autocomplete of banned
 keys

---
 packages/toolkit/src/createSlice.ts | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 236a32509c..1a978c2402 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -852,13 +852,16 @@ interface BuildCreateSliceConfig<
   CreatorMap extends Record<string, RegisteredReducerType>
 > {
   creators?: {
-    [Name in keyof CreatorMap]: ReducerCreator<CreatorMap[Name]>
-  } &
-    Partial<Record<'reducer' | 'preparedReducer', never>>
+    [Name in keyof CreatorMap]: Name extends 'reducer' | 'preparedReducer'
+      ? never
+      : ReducerCreator<CreatorMap[Name]>
+  }
 }
 
 export function buildCreateSlice<
-  CreatorMap extends Record<string, RegisteredReducerType> = {}
+  CreatorMap extends Record<string, RegisteredReducerType> = {
+    asyncThunk?: RegisteredReducerType
+  }
 >({
   creators: creatorMap = {} as any,
 }: BuildCreateSliceConfig<CreatorMap> = {}) {

From bb8cd15ee551b7c574491778d69d3c5cc4855cb2 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.j.durrant@gmail.com>
Date: Sat, 30 Dec 2023 12:18:52 +0000
Subject: [PATCH 022/178] change default back

---
 packages/toolkit/src/createSlice.ts | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 1a978c2402..d714c13cb6 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -859,9 +859,7 @@ interface BuildCreateSliceConfig<
 }
 
 export function buildCreateSlice<
-  CreatorMap extends Record<string, RegisteredReducerType> = {
-    asyncThunk?: RegisteredReducerType
-  }
+  CreatorMap extends Record<string, RegisteredReducerType> = {}
 >({
   creators: creatorMap = {} as any,
 }: BuildCreateSliceConfig<CreatorMap> = {}) {

From 5e299c9bbd15d2d1290eda607d4591bac518ecc4 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.j.durrant@gmail.com>
Date: Sat, 30 Dec 2023 17:07:12 +0000
Subject: [PATCH 023/178] pagination and history creators

---
 packages/toolkit/src/createSlice.ts           |  35 +-
 packages/toolkit/src/index.ts                 |   3 +
 .../toolkit/src/tests/createSlice.test.ts     | 490 ++++++++++++++----
 3 files changed, 431 insertions(+), 97 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index d714c13cb6..b9fd7bfe74 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -269,12 +269,33 @@ interface ReducerDetails {
 export type ReducerCreator<Type extends RegisteredReducerType> = {
   type: Type
   define: SliceReducerCreators[Type]['create']
-  handle<State>(
-    details: ReducerDetails,
-    definition: ReturnType<SliceReducerCreators[Type]['create']>,
-    context: ReducerHandlingContextMethods<State, Type>
-  ): void
-}
+} & (ReturnType<
+  SliceReducerCreators[Type]['create']
+> extends ReducerDefinition<Type>
+  ? {
+      handle<State>(
+        details: ReducerDetails,
+        definition: ReturnType<SliceReducerCreators[Type]['create']>,
+        context: ReducerHandlingContextMethods<State, Type>
+      ): void
+    }
+  : KeysForValueOfType<
+      ReturnType<SliceReducerCreators[Type]['create']>,
+      ReducerDefinition<Type>
+    > extends never
+  ? {}
+  : {
+      handle<State>(
+        details: ReducerDetails,
+        definition: ReturnType<
+          SliceReducerCreators[Type]['create']
+        >[KeysForValueOfType<
+          ReturnType<SliceReducerCreators[Type]['create']>,
+          ReducerDefinition<Type>
+        >],
+        context: ReducerHandlingContextMethods<State, Type>
+      ): void
+    })
 
 interface InjectIntoConfig<NewReducerPath extends string> extends InjectConfig {
   reducerPath?: NewReducerPath
@@ -881,7 +902,7 @@ export function buildCreateSlice<
   }
   for (const [name, creator] of Object.entries<
     ReducerCreator<RegisteredReducerType>
-  >(creatorMap)) {
+  >(creatorMap as any)) {
     if (name === 'reducer' || name === 'preparedReducer') {
       throw new Error('Cannot use reserved creator name: ' + name)
     }
diff --git a/packages/toolkit/src/index.ts b/packages/toolkit/src/index.ts
index 5bc4eb964c..428d2eb598 100644
--- a/packages/toolkit/src/index.ts
+++ b/packages/toolkit/src/index.ts
@@ -84,6 +84,9 @@ export type {
   ReducerDefinition,
   ReducerCreator,
   SliceActionType,
+  CaseReducerDefinition,
+  CaseReducerWithPrepareDefinition,
+  AsyncThunkSliceReducerDefinition,
 } from './createSlice'
 export type { ActionCreatorInvariantMiddlewareOptions } from './actionCreatorInvariantMiddleware'
 export { createActionCreatorInvariantMiddleware } from './actionCreatorInvariantMiddleware'
diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index b183d7c8b0..059c4647f7 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -1,13 +1,18 @@
 import { vi } from 'vitest'
+import type { Patch } from 'immer'
+import { applyPatches, enablePatches, produceWithPatches } from 'immer'
 import type {
   Action,
   AnyListenerPredicate,
   CaseReducer,
+  CaseReducerDefinition,
+  CaseReducerWithPrepareDefinition,
   PayloadAction,
   PayloadActionCreator,
   ReducerCreator,
   ReducerCreators,
   ReducerDefinition,
+  ReducerType,
   SliceActionType,
   SliceCaseReducers,
   ThunkAction,
@@ -26,19 +31,25 @@ import {
   nanoid,
   addListener,
   createListenerMiddleware,
+  createNextState,
 } from '@reduxjs/toolkit'
 import {
   mockConsole,
   createConsole,
   getLog,
 } from 'console-testing-library/pure'
-import type { CaseReducerDefinition } from '../createSlice'
+import type { IfMaybeUndefined } from '../tsHelpers'
+
+enablePatches()
 
 type CreateSlice = typeof createSlice
 
 const loaderCreatorType = Symbol()
 const conditionCreatorType = Symbol()
 const fetchCreatorType = Symbol()
+const paginationCreatorType = Symbol()
+const historyMethodsCreatorType = Symbol()
+const undoableCreatorType = Symbol()
 
 describe('createSlice', () => {
   let restore: () => void
@@ -897,69 +908,6 @@ describe('createSlice', () => {
         context.exposeCaseReducer(reducerName, { started, ended })
       },
     }
-
-    const conditionCreator: ReducerCreator<typeof conditionCreatorType> = {
-      type: conditionCreatorType,
-      define(makePredicate) {
-        return { _reducerDefinitionType: conditionCreatorType, makePredicate }
-      },
-      handle({ reducerName, type }, { makePredicate }, context) {
-        const trigger = createAction(type, (id: string, args: unknown[]) => ({
-          payload: id,
-          meta: { args },
-        }))
-        function thunkCreator(
-          timeout?: number,
-          ...args: any[]
-        ): ThunkAction<
-          Promise<boolean> & { unsubscribe?: UnsubscribeListener },
-          unknown,
-          unknown,
-          UnknownAction
-        > {
-          const predicate = makePredicate(...args)
-          return (dispatch) => {
-            const listenerId = nanoid()
-            let unsubscribe: UnsubscribeListener | undefined = undefined
-            const promise = new Promise<boolean>((resolve, reject) => {
-              unsubscribe = dispatch(
-                addListener({
-                  predicate: (action) =>
-                    trigger.match(action) && action.payload === listenerId,
-                  effect: (_, { condition, unsubscribe, signal }) => {
-                    signal.addEventListener('abort', () => reject(false))
-                    return condition(predicate, timeout)
-                      .then(resolve)
-                      .catch(reject)
-                      .finally(unsubscribe)
-                  },
-                })
-              ) as any
-              dispatch(trigger(listenerId, args))
-            })
-            return Object.assign(promise, { unsubscribe })
-          }
-        }
-        context.exposeAction(reducerName, thunkCreator)
-      },
-    }
-    const fetchCreator: ReducerCreator<typeof fetchCreatorType> = {
-      type: fetchCreatorType,
-      define() {
-        return {
-          start: this.reducer((state) => {
-            state.status = 'loading'
-          }),
-          success: this.reducer<any>((state, action) => {
-            state.data = action.payload
-            state.status = 'finished'
-          }),
-        }
-      },
-      handle() {
-        throw new Error("Shouldn't ever happen")
-      },
-    }
     test('allows passing custom reducer creators, which can add actions and case reducers', () => {
       const createLoaderSlice = buildCreateSlice({
         creators: { loader: loaderCreator },
@@ -1014,6 +962,51 @@ describe('createSlice', () => {
       ])
     })
 
+    const conditionCreator: ReducerCreator<typeof conditionCreatorType> = {
+      type: conditionCreatorType,
+      define(makePredicate) {
+        return { _reducerDefinitionType: conditionCreatorType, makePredicate }
+      },
+      handle({ reducerName, type }, { makePredicate }, context) {
+        const trigger = createAction(type, (id: string, args: unknown[]) => ({
+          payload: id,
+          meta: { args },
+        }))
+        function thunkCreator(
+          timeout?: number,
+          ...args: any[]
+        ): ThunkAction<
+          Promise<boolean> & { unsubscribe?: UnsubscribeListener },
+          unknown,
+          unknown,
+          UnknownAction
+        > {
+          const predicate = makePredicate(...args)
+          return (dispatch) => {
+            const listenerId = nanoid()
+            let unsubscribe: UnsubscribeListener | undefined = undefined
+            const promise = new Promise<boolean>((resolve, reject) => {
+              unsubscribe = dispatch(
+                addListener({
+                  predicate: (action) =>
+                    trigger.match(action) && action.payload === listenerId,
+                  effect: (_, { condition, unsubscribe, signal }) => {
+                    signal.addEventListener('abort', () => reject(false))
+                    return condition(predicate, timeout)
+                      .then(resolve)
+                      .catch(reject)
+                      .finally(unsubscribe)
+                  },
+                })
+              ) as any
+              dispatch(trigger(listenerId, args))
+            })
+            return Object.assign(promise, { unsubscribe })
+          }
+        }
+        context.exposeAction(reducerName, thunkCreator)
+      },
+    }
     function withStatus<P extends PromiseLike<any>>(promiseLike: P) {
       const assigned = Object.assign(promiseLike, {
         status: 'pending' as 'pending' | 'fulfilled' | 'rejected',
@@ -1074,40 +1067,246 @@ describe('createSlice', () => {
       await expect(promise).resolves.toBe(true)
       expect(promise.status).toBe('fulfilled')
     })
-    test('creators can return multiple definitions to be spread', () => {
-      const createFetchSlice = buildCreateSlice({
-        creators: { fetchReducers: fetchCreator },
+    describe('creators can return multiple definitions to be spread', () => {
+      const fetchCreator: ReducerCreator<typeof fetchCreatorType> = {
+        type: fetchCreatorType,
+        define() {
+          return {
+            start: this.reducer((state: FetchState<unknown>) => {
+              state.status = 'loading'
+            }),
+            success: this.reducer<any>((state: FetchState<unknown>, action) => {
+              state.data = action.payload
+              state.status = 'finished'
+            }),
+          }
+        },
+      }
+      test('fetch slice', () => {
+        const createFetchSlice = buildCreateSlice({
+          creators: { fetchReducers: fetchCreator },
+        })
+
+        const fetchSlice = createFetchSlice({
+          name: 'fetch',
+          initialState: { status: 'loading' } as FetchState<string>,
+          reducers: (create) => ({
+            ...create.fetchReducers(),
+            magic: create.reducer((state) => {
+              state.status = 'finished'
+              state.data = 'hocus pocus'
+            }),
+          }),
+        })
+
+        const { start, success, magic } = fetchSlice.actions
+
+        expect(start).toEqual(expect.any(Function))
+        expect(start.type).toBe('fetch/start')
       })
+      test('pagination slice', () => {
+        const paginationCreator: ReducerCreator<typeof paginationCreatorType> =
+          {
+            type: paginationCreatorType,
+            define() {
+              return {
+                nextPage: this.reducer((state: PaginationState) => {
+                  state.page++
+                }),
+                previousPage: this.reducer((state: PaginationState) => {
+                  state.page = Math.max(0, state.page - 1)
+                }),
+                goToPage: this.reducer<number>(
+                  (state: PaginationState, action) => {
+                    state.page = action.payload
+                  }
+                ),
+              }
+            },
+          }
+        const createPaginationSlice = buildCreateSlice({
+          creators: { paginationReducers: paginationCreator },
+        })
 
-      const fetchSlice = createFetchSlice({
-        name: 'fetch',
-        initialState: { status: 'loading' } as FetchState<string>,
-        reducers: (create) => ({
-          ...create.fetchReducers(),
-          magic: create.reducer((state) => {
-            state.status = 'finished'
-            state.data = 'hocus pocus'
+        const paginationSlice = createPaginationSlice({
+          name: 'pagination',
+          initialState: {
+            page: 1,
+            hasMoreData: true,
+          },
+          reducers: (create) => ({
+            ...create.paginationReducers(),
+            noMoreData: create.reducer((state) => {
+              state.hasMoreData = false
+            }),
           }),
-        }),
+          selectors: {
+            selectPage: (state) => state.page,
+            selectHasMoreData: (state) => state.hasMoreData,
+          },
+        })
+        const { nextPage, previousPage, goToPage } = paginationSlice.actions
+        const { selectHasMoreData, selectPage } = paginationSlice.selectors
+
+        const store = configureStore({
+          reducer: { [paginationSlice.reducerPath]: paginationSlice.reducer },
+        })
+
+        expect(selectPage(store.getState())).toBe(1)
+
+        store.dispatch(nextPage())
+
+        expect(selectPage(store.getState())).toBe(2)
       })
+      test('history slice', () => {
+        function getInitialHistoryState<T>(initialState: T): HistoryState<T> {
+          return {
+            past: [],
+            present: initialState,
+            future: [],
+          }
+        }
+        const historyMethodsCreator: ReducerCreator<
+          typeof historyMethodsCreatorType
+        > = {
+          type: historyMethodsCreatorType,
+          define() {
+            return {
+              undo: this.reducer((state: HistoryState<unknown>) => {
+                const historyEntry = state.past.pop()
+                if (historyEntry) {
+                  applyPatches(state, historyEntry.undo)
+                  state.future.unshift(historyEntry)
+                }
+              }),
+              redo: this.reducer((state: HistoryState<unknown>) => {
+                const historyEntry = state.future.shift()
+                if (historyEntry) {
+                  applyPatches(state, historyEntry.redo)
+                  state.past.push(historyEntry)
+                }
+              }),
+              reset: {
+                _reducerDefinitionType: historyMethodsCreatorType,
+                type: 'reset',
+              },
+            }
+          },
+          handle({ reducerName, type }, definition, context) {
+            if (definition.type !== 'reset') {
+              throw new Error('unrecognised definition')
+            }
+            const resetAction = createAction(type)
+            const resetReducer = () => context.getInitialState()
+            context
+              .addCase(resetAction, resetReducer)
+              .exposeAction(reducerName, resetAction)
+              .exposeCaseReducer(reducerName, resetReducer)
+          },
+        }
+
+        const undoableCreator: ReducerCreator<typeof undoableCreatorType> = {
+          type: undoableCreatorType,
+          define(
+            this: ReducerCreators<HistoryState<any>, {}>,
+            reducer: CaseReducer<any, PayloadAction<any>>
+          ) {
+            return this.preparedReducer(
+              (payload: any, options?: UndoableOptions) => ({
+                payload,
+                meta: options,
+              }),
+              (state, action) => {
+                const [nextState, redoPatch, undoPatch] = produceWithPatches(
+                  state,
+                  (draft) => {
+                    const result = reducer(draft.present, action)
+                    if (typeof result !== 'undefined') {
+                      draft.present = result
+                    }
+                  }
+                )
+                let finalState = nextState
+                const undoable = action.meta?.undoable ?? true
+                if (undoable) {
+                  finalState = createNextState(finalState, (draft) => {
+                    draft.past.push({
+                      undo: undoPatch,
+                      redo: redoPatch,
+                    })
+                    draft.future = []
+                  })
+                }
+                return finalState
+              }
+            )
+          },
+        }
 
-      const { start, success, magic } = fetchSlice.actions
+        const createHistorySlice = buildCreateSlice({
+          creators: {
+            historyMethods: historyMethodsCreator,
+            undoable: undoableCreator,
+          },
+        })
 
-      expect(start).toEqual(expect.any(Function))
-      expect(start.type).toBe('fetch/start')
-    })
-    test.skip('creators can discourage their use if state is incompatible (types only)', () => {
-      const createFetchSlice = buildCreateSlice({
-        creators: { fetchReducers: fetchCreator },
+        const historySlice = createHistorySlice({
+          name: 'history',
+          initialState: getInitialHistoryState({ value: 1 }),
+          reducers: (create) => ({
+            ...create.historyMethods(),
+            increment: create.undoable((state) => {
+              state.value++
+            }),
+            incrementBy: create.undoable<number>((state, action) => {
+              state.value += action.payload
+            }),
+          }),
+          selectors: {
+            selectValue: (state) => state.present.value,
+          },
+        })
+        const { increment, incrementBy, undo, redo, reset } =
+          historySlice.actions
+        const { selectValue } = historySlice.selectors
+
+        const store = configureStore({
+          reducer: { [historySlice.reducerPath]: historySlice.reducer },
+        })
+
+        expect(selectValue(store.getState())).toBe(1)
+
+        store.dispatch(increment())
+        expect(selectValue(store.getState())).toBe(2)
+
+        store.dispatch(undo())
+        expect(selectValue(store.getState())).toBe(1)
+
+        store.dispatch(incrementBy(3))
+        expect(selectValue(store.getState())).toBe(4)
+
+        store.dispatch(undo())
+        expect(selectValue(store.getState())).toBe(1)
+
+        store.dispatch(redo())
+        expect(selectValue(store.getState())).toBe(4)
+
+        store.dispatch(reset())
+        expect(selectValue(store.getState())).toBe(1)
       })
+      test.skip('creators can discourage their use if state is incompatible (types only)', () => {
+        const createFetchSlice = buildCreateSlice({
+          creators: { fetchReducers: fetchCreator },
+        })
 
-      const counterSlice = createFetchSlice({
-        name: 'counter',
-        initialState: { value: 0 },
-        reducers: (create) => ({
-          // @ts-expect-error
-          ...create.fetchReducers(),
-        }),
+        const counterSlice = createFetchSlice({
+          name: 'counter',
+          initialState: { value: 0 },
+          reducers: (create) => ({
+            // @ts-expect-error
+            ...create.fetchReducers(),
+          }),
+        })
       })
     })
   })
@@ -1124,11 +1323,33 @@ interface FetchState<T> {
   status: 'loading' | 'finished' | 'error'
 }
 
+interface PaginationState {
+  page: number
+}
+
+interface PatchesState {
+  undo: Patch[]
+  redo: Patch[]
+}
+
+interface HistoryState<T> {
+  past: PatchesState[]
+  present: T
+  future: PatchesState[]
+}
+
+interface UndoableOptions {
+  undoable?: boolean
+}
+
 declare module '@reduxjs/toolkit' {
   export interface ReducerTypes {
     [loaderCreatorType]: true
     [conditionCreatorType]: true
     [fetchCreatorType]: true
+    [paginationCreatorType]: true
+    [historyMethodsCreatorType]: true
+    [undoableCreatorType]: true
   }
   export interface SliceReducerCreators<
     State = any,
@@ -1205,5 +1426,94 @@ declare module '@reduxjs/toolkit' {
       actions: {}
       caseReducers: {}
     }
+    [paginationCreatorType]: {
+      create(this: ReducerCreators<State, {}>): State extends PaginationState
+        ? {
+            nextPage: CaseReducerDefinition<State, PayloadAction>
+            previousPage: CaseReducerDefinition<State, PayloadAction>
+            goToPage: CaseReducerDefinition<State, PayloadAction<number>>
+          }
+        : never
+      actions: {}
+      caseReducers: {}
+    }
+    [historyMethodsCreatorType]: {
+      create(
+        this: ReducerCreators<State, {}>
+      ): State extends HistoryState<unknown>
+        ? {
+            undo: CaseReducerDefinition<State, PayloadAction>
+            redo: CaseReducerDefinition<State, PayloadAction>
+            reset: ReducerDefinition<typeof historyMethodsCreatorType> & {
+              type: 'reset'
+            }
+          }
+        : never
+      actions: {
+        [ReducerName in keyof CaseReducers as CaseReducers[ReducerName] extends ReducerDefinition<
+          typeof historyMethodsCreatorType
+        >
+          ? ReducerName
+          : never]: CaseReducers[ReducerName] extends { type: 'reset' }
+          ? PayloadActionCreator<void, SliceActionType<Name, ReducerName>>
+          : never
+      }
+      caseReducers: {
+        [ReducerName in keyof CaseReducers as CaseReducers[ReducerName] extends ReducerDefinition<
+          typeof historyMethodsCreatorType
+        >
+          ? ReducerName
+          : never]: CaseReducers[ReducerName] extends { type: 'reset' }
+          ? CaseReducer<State, PayloadAction>
+          : never
+      }
+    }
+    [undoableCreatorType]: {
+      create(
+        this: ReducerCreators<State, {}>,
+        reducer: CaseReducer<
+          State extends HistoryState<infer Data> ? Data : never,
+          PayloadAction
+        >
+      ): State extends HistoryState<unknown>
+        ? ReducerDefinition<ReducerType.reducerWithPrepare> & {
+            prepare: (
+              payload?: undefined,
+              options?: UndoableOptions
+            ) => { payload: undefined; meta?: UndoableOptions }
+            reducer: CaseReducer<
+              State,
+              PayloadAction<void, string, UndoableOptions | undefined>
+            >
+          }
+        : never
+      create<Payload>(
+        this: ReducerCreators<State, {}>,
+        reducer: CaseReducer<
+          State extends HistoryState<infer Data> ? Data : never,
+          PayloadAction<Payload>
+        >
+      ): State extends HistoryState<unknown>
+        ? ReducerDefinition<ReducerType.reducerWithPrepare> & {
+            prepare: IfMaybeUndefined<
+              Payload,
+              (
+                payload?: Payload,
+                options?: UndoableOptions
+              ) => { payload: Payload; meta?: UndoableOptions },
+              (
+                payload: Payload,
+                options?: UndoableOptions
+              ) => { payload: Payload; meta?: UndoableOptions }
+            >
+            reducer: CaseReducer<
+              State,
+              PayloadAction<Payload, string, UndoableOptions | undefined>
+            >
+          }
+        : never
+      actions: {}
+      caseReducers: {}
+    }
   }
 }

From 45fa03ae1e1e71b0f3e5a60bfe0fbcc130fd09a1 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.j.durrant@gmail.com>
Date: Sat, 30 Dec 2023 21:47:09 +0000
Subject: [PATCH 024/178] add PreparedCaseReducerDefinition

---
 packages/toolkit/src/createSlice.ts           | 20 ++++++++------
 packages/toolkit/src/index.ts                 |  2 +-
 .../toolkit/src/tests/createSlice.test.ts     | 26 +++++++------------
 3 files changed, 23 insertions(+), 25 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index b9fd7bfe74..9613dd6271 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -88,14 +88,7 @@ export interface SliceReducerCreators<
         State,
         ReturnType<_ActionCreatorWithPreparedPayload<Prepare>>
       >
-    ): {
-      _reducerDefinitionType: ReducerType.reducerWithPrepare
-      prepare: Prepare
-      reducer: CaseReducer<
-        State,
-        ReturnType<_ActionCreatorWithPreparedPayload<Prepare>>
-      >
-    }
+    ): PreparedCaseReducerDefinition<State, Prepare>
     actions: {
       [ReducerName in keyof CaseReducers as CaseReducers[ReducerName] extends CaseReducerWithPrepare<
         State,
@@ -547,6 +540,17 @@ export interface CaseReducerWithPrepareDefinition<
 > extends CaseReducerWithPrepare<State, Action>,
     ReducerDefinition<ReducerType.reducerWithPrepare> {}
 
+export interface PreparedCaseReducerDefinition<
+  State,
+  Prepare extends PrepareAction<any>
+> extends ReducerDefinition<ReducerType.reducerWithPrepare> {
+  prepare: Prepare
+  reducer: CaseReducer<
+    State,
+    ReturnType<_ActionCreatorWithPreparedPayload<Prepare>>
+  >
+}
+
 export interface AsyncThunkSliceReducerConfig<
   State,
   ThunkArg extends any,
diff --git a/packages/toolkit/src/index.ts b/packages/toolkit/src/index.ts
index 428d2eb598..e75f7629eb 100644
--- a/packages/toolkit/src/index.ts
+++ b/packages/toolkit/src/index.ts
@@ -85,7 +85,7 @@ export type {
   ReducerCreator,
   SliceActionType,
   CaseReducerDefinition,
-  CaseReducerWithPrepareDefinition,
+  PreparedCaseReducerDefinition,
   AsyncThunkSliceReducerDefinition,
 } from './createSlice'
 export type { ActionCreatorInvariantMiddlewareOptions } from './actionCreatorInvariantMiddleware'
diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index 059c4647f7..204f8fc4e1 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -6,7 +6,7 @@ import type {
   AnyListenerPredicate,
   CaseReducer,
   CaseReducerDefinition,
-  CaseReducerWithPrepareDefinition,
+  PreparedCaseReducerDefinition,
   PayloadAction,
   PayloadActionCreator,
   ReducerCreator,
@@ -1212,7 +1212,7 @@ describe('createSlice', () => {
             reducer: CaseReducer<any, PayloadAction<any>>
           ) {
             return this.preparedReducer(
-              (payload: any, options?: UndoableOptions) => ({
+              (payload?: any, options?: UndoableOptions) => ({
                 payload,
                 meta: options,
               }),
@@ -1476,16 +1476,13 @@ declare module '@reduxjs/toolkit' {
           PayloadAction
         >
       ): State extends HistoryState<unknown>
-        ? ReducerDefinition<ReducerType.reducerWithPrepare> & {
-            prepare: (
+        ? PreparedCaseReducerDefinition<
+            State,
+            (
               payload?: undefined,
               options?: UndoableOptions
             ) => { payload: undefined; meta?: UndoableOptions }
-            reducer: CaseReducer<
-              State,
-              PayloadAction<void, string, UndoableOptions | undefined>
-            >
-          }
+          >
         : never
       create<Payload>(
         this: ReducerCreators<State, {}>,
@@ -1494,8 +1491,9 @@ declare module '@reduxjs/toolkit' {
           PayloadAction<Payload>
         >
       ): State extends HistoryState<unknown>
-        ? ReducerDefinition<ReducerType.reducerWithPrepare> & {
-            prepare: IfMaybeUndefined<
+        ? PreparedCaseReducerDefinition<
+            State,
+            IfMaybeUndefined<
               Payload,
               (
                 payload?: Payload,
@@ -1506,11 +1504,7 @@ declare module '@reduxjs/toolkit' {
                 options?: UndoableOptions
               ) => { payload: Payload; meta?: UndoableOptions }
             >
-            reducer: CaseReducer<
-              State,
-              PayloadAction<Payload, string, UndoableOptions | undefined>
-            >
-          }
+          >
         : never
       actions: {}
       caseReducers: {}

From 6376f8b2b1707d26f20acac632198295a08761a1 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.j.durrant@gmail.com>
Date: Sat, 30 Dec 2023 22:13:40 +0000
Subject: [PATCH 025/178] separate reducer factory and prepare for more
 flexibility

---
 .../toolkit/src/tests/createSlice.test.ts     | 144 ++++++++----------
 1 file changed, 66 insertions(+), 78 deletions(-)

diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index 204f8fc4e1..61b046e389 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -38,7 +38,7 @@ import {
   createConsole,
   getLog,
 } from 'console-testing-library/pure'
-import type { IfMaybeUndefined } from '../tsHelpers'
+import type { IfMaybeUndefined, NoInfer } from '../tsHelpers'
 
 enablePatches()
 
@@ -1205,42 +1205,41 @@ describe('createSlice', () => {
           },
         }
 
-        const undoableCreator: ReducerCreator<typeof undoableCreatorType> = {
-          type: undoableCreatorType,
-          define(
-            this: ReducerCreators<HistoryState<any>, {}>,
-            reducer: CaseReducer<any, PayloadAction<any>>
-          ) {
-            return this.preparedReducer(
-              (payload?: any, options?: UndoableOptions) => ({
-                payload,
-                meta: options,
-              }),
-              (state, action) => {
-                const [nextState, redoPatch, undoPatch] = produceWithPatches(
-                  state,
-                  (draft) => {
-                    const result = reducer(draft.present, action)
-                    if (typeof result !== 'undefined') {
-                      draft.present = result
-                    }
-                  }
-                )
-                let finalState = nextState
-                const undoable = action.meta?.undoable ?? true
-                if (undoable) {
-                  finalState = createNextState(finalState, (draft) => {
-                    draft.past.push({
-                      undo: undoPatch,
-                      redo: redoPatch,
-                    })
-                    draft.future = []
-                  })
+        const makeUndoable = function makeUndoable(reducer) {
+          return function wrappedReducer(state: HistoryState<any>, action) {
+            const [nextState, redoPatch, undoPatch] = produceWithPatches(
+              state,
+              (draft) => {
+                const result = reducer(draft.present, action)
+                if (typeof result !== 'undefined') {
+                  draft.present = result
                 }
-                return finalState
               }
             )
-          },
+            let finalState = nextState
+            const undoable = action.meta?.undoable ?? true
+            if (undoable) {
+              finalState = createNextState(finalState, (draft) => {
+                draft.past.push({
+                  undo: undoPatch,
+                  redo: redoPatch,
+                })
+                draft.future = []
+              })
+            }
+            return finalState
+          }
+        } as ReducerCreator<typeof undoableCreatorType>['define']
+
+        makeUndoable.prepare = (() =>
+          (payload: unknown, options?: UndoableOptions) => ({
+            payload,
+            meta: options,
+          })) as any
+
+        const undoableCreator: ReducerCreator<typeof undoableCreatorType> = {
+          type: undoableCreatorType,
+          define: makeUndoable,
         }
 
         const createHistorySlice = buildCreateSlice({
@@ -1255,20 +1254,30 @@ describe('createSlice', () => {
           initialState: getInitialHistoryState({ value: 1 }),
           reducers: (create) => ({
             ...create.historyMethods(),
-            increment: create.undoable((state) => {
-              state.value++
-            }),
-            incrementBy: create.undoable<number>((state, action) => {
-              state.value += action.payload
-            }),
+            increment: create.preparedReducer(
+              (options?: UndoableOptions) => ({
+                payload: undefined,
+                meta: options,
+              }),
+              create.undoable((state) => {
+                state.value++
+              })
+            ),
+            incrementBy: create.preparedReducer(
+              create.undoable.prepare<number>(),
+              create.undoable((state, action) => {
+                state.value += action.payload
+              })
+            ),
           }),
           selectors: {
             selectValue: (state) => state.present.value,
           },
         })
-        const { increment, incrementBy, undo, redo, reset } =
-          historySlice.actions
-        const { selectValue } = historySlice.selectors
+        const {
+          actions: { increment, incrementBy, undo, redo, reset },
+          selectors: { selectValue },
+        } = historySlice
 
         const store = configureStore({
           reducer: { [historySlice.reducerPath]: historySlice.reducer },
@@ -1469,43 +1478,22 @@ declare module '@reduxjs/toolkit' {
       }
     }
     [undoableCreatorType]: {
-      create(
-        this: ReducerCreators<State, {}>,
-        reducer: CaseReducer<
-          State extends HistoryState<infer Data> ? Data : never,
-          PayloadAction
-        >
-      ): State extends HistoryState<unknown>
-        ? PreparedCaseReducerDefinition<
-            State,
-            (
-              payload?: undefined,
-              options?: UndoableOptions
-            ) => { payload: undefined; meta?: UndoableOptions }
+      create: {
+        <A extends Action & { meta?: UndoableOptions }>(
+          this: ReducerCreators<State, {}>,
+          reducer: CaseReducer<
+            State extends HistoryState<infer Data> ? Data : never,
+            NoInfer<A>
           >
-        : never
-      create<Payload>(
-        this: ReducerCreators<State, {}>,
-        reducer: CaseReducer<
-          State extends HistoryState<infer Data> ? Data : never,
-          PayloadAction<Payload>
-        >
-      ): State extends HistoryState<unknown>
-        ? PreparedCaseReducerDefinition<
-            State,
-            IfMaybeUndefined<
-              Payload,
-              (
-                payload?: Payload,
-                options?: UndoableOptions
-              ) => { payload: Payload; meta?: UndoableOptions },
-              (
-                payload: Payload,
-                options?: UndoableOptions
-              ) => { payload: Payload; meta?: UndoableOptions }
-            >
+        ): CaseReducer<State, A>
+        prepare<P>(): (
+          ...args: IfMaybeUndefined<
+            P,
+            [payload?: P, options?: UndoableOptions],
+            [payload: P, options?: UndoableOptions]
           >
-        : never
+        ) => { payload: P; meta: UndoableOptions | undefined }
+      }
       actions: {}
       caseReducers: {}
     }

From 821a73251c14b7092bf0ec9942c5dd807024f2b6 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.j.durrant@gmail.com>
Date: Sat, 30 Dec 2023 22:17:45 +0000
Subject: [PATCH 026/178] rename prepare and add withoutPayload prepare

---
 .../toolkit/src/tests/createSlice.test.ts     | 19 ++++++++++++-------
 1 file changed, 12 insertions(+), 7 deletions(-)

diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index 61b046e389..cac877df7d 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -1231,7 +1231,11 @@ describe('createSlice', () => {
           }
         } as ReducerCreator<typeof undoableCreatorType>['define']
 
-        makeUndoable.prepare = (() =>
+        makeUndoable.withoutPayload = () => (options) => ({
+          payload: undefined,
+          meta: options,
+        })
+        makeUndoable.withPayload = (() =>
           (payload: unknown, options?: UndoableOptions) => ({
             payload,
             meta: options,
@@ -1255,16 +1259,13 @@ describe('createSlice', () => {
           reducers: (create) => ({
             ...create.historyMethods(),
             increment: create.preparedReducer(
-              (options?: UndoableOptions) => ({
-                payload: undefined,
-                meta: options,
-              }),
+              create.undoable.withoutPayload(),
               create.undoable((state) => {
                 state.value++
               })
             ),
             incrementBy: create.preparedReducer(
-              create.undoable.prepare<number>(),
+              create.undoable.withPayload<number>(),
               create.undoable((state, action) => {
                 state.value += action.payload
               })
@@ -1486,7 +1487,11 @@ declare module '@reduxjs/toolkit' {
             NoInfer<A>
           >
         ): CaseReducer<State, A>
-        prepare<P>(): (
+        withoutPayload(): (options?: UndoableOptions) => {
+          payload: undefined
+          meta: UndoableOptions | undefined
+        }
+        withPayload<P>(): (
           ...args: IfMaybeUndefined<
             P,
             [payload?: P, options?: UndoableOptions],

From 8e1da7bfa5bdbe440385d40d418ea8d940b0eb4c Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.j.durrant@gmail.com>
Date: Sat, 30 Dec 2023 22:47:30 +0000
Subject: [PATCH 027/178] Add checking/autocomplete for asyncThunk creator key

---
 packages/toolkit/src/createSlice.ts                | 2 +-
 packages/toolkit/src/tests/createSlice.typetest.ts | 6 ++++++
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 9613dd6271..0c49c2fcc0 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -880,7 +880,7 @@ interface BuildCreateSliceConfig<
     [Name in keyof CreatorMap]: Name extends 'reducer' | 'preparedReducer'
       ? never
       : ReducerCreator<CreatorMap[Name]>
-  }
+  } & { asyncThunk?: ReducerCreator<ReducerType.asyncThunk> }
 }
 
 export function buildCreateSlice<
diff --git a/packages/toolkit/src/tests/createSlice.typetest.ts b/packages/toolkit/src/tests/createSlice.typetest.ts
index 79bce3f311..c3e613a3c8 100644
--- a/packages/toolkit/src/tests/createSlice.typetest.ts
+++ b/packages/toolkit/src/tests/createSlice.typetest.ts
@@ -10,7 +10,9 @@ import type {
   CaseReducer,
   PayloadAction,
   PayloadActionCreator,
+  ReducerCreator,
   ReducerCreators,
+  ReducerType,
   SerializedError,
   SliceCaseReducers,
   ThunkDispatch,
@@ -875,4 +877,8 @@ const value = actionCreators.anyKey
   buildCreateSlice({ creators: { reducer: asyncThunkCreator } })
   // @ts-expect-error prevent passing preparedReducer key
   buildCreateSlice({ creators: { preparedReducer: asyncThunkCreator } })
+
+  const wrongCreator = {} as ReducerCreator<ReducerType.reducer>
+  // @ts-expect-error asyncThunk must be ReducerType.asyncThunk creator
+  buildCreateSlice({ creators: { asyncThunk: wrongCreator } })
 }

From 00ac1b12ba4b2b5802b2a5e25c82130ec3031216 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.j.durrant@gmail.com>
Date: Sat, 30 Dec 2023 23:27:34 +0000
Subject: [PATCH 028/178] export reducer creators and reuse handlers

---
 packages/toolkit/src/createSlice.ts           | 35 ++++++++++---------
 packages/toolkit/src/index.ts                 |  2 ++
 .../toolkit/src/tests/createSlice.test.ts     | 19 +++++-----
 3 files changed, 30 insertions(+), 26 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 0c49c2fcc0..013227c147 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -786,7 +786,7 @@ function getType(slice: string, actionKey: string): string {
   return `${slice}/${actionKey}`
 }
 
-const reducerCreator: ReducerCreator<ReducerType.reducer> = {
+export const reducerCreator: ReducerCreator<ReducerType.reducer> = {
   type: ReducerType.reducer,
   define(caseReducer: CaseReducer<any, any>) {
     return Object.assign(
@@ -810,22 +810,23 @@ const reducerCreator: ReducerCreator<ReducerType.reducer> = {
   },
 }
 
-const preparedReducerCreator: ReducerCreator<ReducerType.reducerWithPrepare> = {
-  type: ReducerType.reducerWithPrepare,
-  define(prepare, reducer) {
-    return {
-      _reducerDefinitionType: ReducerType.reducerWithPrepare,
-      prepare,
-      reducer,
-    }
-  },
-  handle({ type, reducerName }, { prepare, reducer }, context) {
-    context
-      .addCase(type, reducer)
-      .exposeCaseReducer(reducerName, reducer)
-      .exposeAction(reducerName, createAction(type, prepare))
-  },
-}
+export const preparedReducerCreator: ReducerCreator<ReducerType.reducerWithPrepare> =
+  {
+    type: ReducerType.reducerWithPrepare,
+    define(prepare, reducer) {
+      return {
+        _reducerDefinitionType: ReducerType.reducerWithPrepare,
+        prepare,
+        reducer,
+      }
+    },
+    handle({ type, reducerName }, { prepare, reducer }, context) {
+      context
+        .addCase(type, reducer)
+        .exposeCaseReducer(reducerName, reducer)
+        .exposeAction(reducerName, createAction(type, prepare))
+    },
+  }
 
 export const asyncThunkCreator: ReducerCreator<ReducerType.asyncThunk> = {
   type: ReducerType.asyncThunk,
diff --git a/packages/toolkit/src/index.ts b/packages/toolkit/src/index.ts
index e75f7629eb..1d4ea47a27 100644
--- a/packages/toolkit/src/index.ts
+++ b/packages/toolkit/src/index.ts
@@ -65,6 +65,8 @@ export {
   // js
   createSlice,
   buildCreateSlice,
+  reducerCreator,
+  preparedReducerCreator,
   asyncThunkCreator,
   ReducerType,
 } from './createSlice'
diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index cac877df7d..7d34afa1be 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -6,13 +6,11 @@ import type {
   AnyListenerPredicate,
   CaseReducer,
   CaseReducerDefinition,
-  PreparedCaseReducerDefinition,
   PayloadAction,
   PayloadActionCreator,
   ReducerCreator,
   ReducerCreators,
   ReducerDefinition,
-  ReducerType,
   SliceActionType,
   SliceCaseReducers,
   ThunkAction,
@@ -32,6 +30,8 @@ import {
   addListener,
   createListenerMiddleware,
   createNextState,
+  reducerCreator,
+  ReducerType,
 } from '@reduxjs/toolkit'
 import {
   mockConsole,
@@ -1192,16 +1192,17 @@ describe('createSlice', () => {
               },
             }
           },
-          handle({ reducerName, type }, definition, context) {
+          handle(details, definition, context) {
             if (definition.type !== 'reset') {
               throw new Error('unrecognised definition')
             }
-            const resetAction = createAction(type)
-            const resetReducer = () => context.getInitialState()
-            context
-              .addCase(resetAction, resetReducer)
-              .exposeAction(reducerName, resetAction)
-              .exposeCaseReducer(reducerName, resetReducer)
+            reducerCreator.handle(
+              details,
+              Object.assign(() => context.getInitialState(), {
+                _reducerDefinitionType: ReducerType.reducer as const,
+              }),
+              context
+            )
           },
         }
 

From 7753cec18a440c28551d5855319b94f271d75053 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.j.durrant@gmail.com>
Date: Sat, 30 Dec 2023 23:46:26 +0000
Subject: [PATCH 029/178] Properly extract possible reducer definitions for a
 given handler

---
 packages/toolkit/src/createSlice.ts | 42 ++++++++++++++---------------
 1 file changed, 21 insertions(+), 21 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 013227c147..60c4767cb8 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -259,33 +259,29 @@ interface ReducerDetails {
   type: string
 }
 
+export type ReducerDefinitionsForType<Type extends RegisteredReducerType> = {
+  [T in keyof SliceReducerCreators]-?:
+    | Extract<
+        ReturnType<SliceReducerCreators[Type]['create']>,
+        ReducerDefinition<Type>
+      >
+    | {
+        [K in keyof ReturnType<SliceReducerCreators[T]['create']>]-?: Extract<
+          ReturnType<SliceReducerCreators[T]['create']>[K],
+          ReducerDefinition<Type>
+        >
+      }[keyof ReturnType<SliceReducerCreators[T]['create']>]
+}[keyof SliceReducerCreators]
+
 export type ReducerCreator<Type extends RegisteredReducerType> = {
   type: Type
   define: SliceReducerCreators[Type]['create']
-} & (ReturnType<
-  SliceReducerCreators[Type]['create']
-> extends ReducerDefinition<Type>
-  ? {
-      handle<State>(
-        details: ReducerDetails,
-        definition: ReturnType<SliceReducerCreators[Type]['create']>,
-        context: ReducerHandlingContextMethods<State, Type>
-      ): void
-    }
-  : KeysForValueOfType<
-      ReturnType<SliceReducerCreators[Type]['create']>,
-      ReducerDefinition<Type>
-    > extends never
+} & (ReducerDefinitionsForType<Type> extends never
   ? {}
   : {
       handle<State>(
         details: ReducerDetails,
-        definition: ReturnType<
-          SliceReducerCreators[Type]['create']
-        >[KeysForValueOfType<
-          ReturnType<SliceReducerCreators[Type]['create']>,
-          ReducerDefinition<Type>
-        >],
+        definition: ReducerDefinitionsForType<Type>,
         context: ReducerHandlingContextMethods<State, Type>
       ): void
     })
@@ -802,7 +798,11 @@ export const reducerCreator: ReducerCreator<ReducerType.reducer> = {
       } as const
     )
   },
-  handle({ type, reducerName }, reducer, context) {
+  handle(
+    { type, reducerName },
+    reducer: CaseReducer<any, PayloadAction<any>>,
+    context
+  ) {
     context
       .addCase(type, reducer)
       .exposeCaseReducer(reducerName, reducer)

From 4d81deac8fa00a9c84feb48b390e17c89c78ba95 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.j.durrant@gmail.com>
Date: Sat, 30 Dec 2023 23:54:48 +0000
Subject: [PATCH 030/178] prevent overwriting handlers for reducer and
 reducerWithPrepare

---
 packages/toolkit/src/createSlice.ts | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 60c4767cb8..7ce0157dfa 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -911,6 +911,12 @@ export function buildCreateSlice<
     if (name === 'reducer' || name === 'preparedReducer') {
       throw new Error('Cannot use reserved creator name: ' + name)
     }
+    if (
+      creator.type === ReducerType.reducer ||
+      creator.type === ReducerType.reducerWithPrepare
+    ) {
+      throw new Error('Cannot use reserved creator type: ' + creator.type)
+    }
     creators[name] = creator.define
     handlers[creator.type] = creator.handle
   }

From 6d0e07c87019b485233c5375752b57ada10278ea Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.j.durrant@gmail.com>
Date: Sat, 30 Dec 2023 23:57:20 +0000
Subject: [PATCH 031/178] remove unnecessary generic

---
 packages/toolkit/src/createSlice.ts | 17 +++++++----------
 1 file changed, 7 insertions(+), 10 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 7ce0157dfa..89a1fc7218 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -175,10 +175,7 @@ interface ReducerHandlingContext<State> {
   actionCreators: Record<string, any>
 }
 
-interface ReducerHandlingContextMethods<
-  State,
-  ReducerType extends RegisteredReducerType = RegisteredReducerType
-> {
+interface ReducerHandlingContextMethods<State> {
   /**
    * Adds a case reducer to handle a single action type.
    * @param actionCreator - Either a plain action type string, or an action creator generated by [`createAction`](./createAction) that can be used to determine the action type.
@@ -187,7 +184,7 @@ interface ReducerHandlingContextMethods<
   addCase<ActionCreator extends TypedActionCreator<string>>(
     actionCreator: ActionCreator,
     reducer: CaseReducer<State, ReturnType<ActionCreator>>
-  ): ReducerHandlingContextMethods<State, ReducerType>
+  ): ReducerHandlingContextMethods<State>
   /**
    * Adds a case reducer to handle a single action type.
    * @param actionCreator - Either a plain action type string, or an action creator generated by [`createAction`](./createAction) that can be used to determine the action type.
@@ -196,7 +193,7 @@ interface ReducerHandlingContextMethods<
   addCase<Type extends string, A extends Action<Type>>(
     type: Type,
     reducer: CaseReducer<State, A>
-  ): ReducerHandlingContextMethods<State, ReducerType>
+  ): ReducerHandlingContextMethods<State>
 
   /**
    * Allows you to match incoming actions against your own filter function instead of only the `action.type` property.
@@ -212,7 +209,7 @@ interface ReducerHandlingContextMethods<
   addMatcher<A>(
     matcher: TypeGuard<A>,
     reducer: CaseReducer<State, A extends Action ? A : A & Action>
-  ): ReducerHandlingContextMethods<State, ReducerType>
+  ): ReducerHandlingContextMethods<State>
   /**
    * Add an action to be exposed under the final `slice.actions` key.
    * @param name The key to be exposed as.
@@ -228,7 +225,7 @@ interface ReducerHandlingContextMethods<
     name: string,
     // TODO: see if there's a way to get the actual type cleanly
     actionCreator: unknown
-  ): ReducerHandlingContextMethods<State, ReducerType>
+  ): ReducerHandlingContextMethods<State>
   /**
    * Add a case reducer to be exposed under the final `slice.caseReducers` key.
    * @param name The key to be exposed as.
@@ -244,7 +241,7 @@ interface ReducerHandlingContextMethods<
     name: string,
     // TODO: see if there's a way to get the actual type cleanly
     reducer: unknown
-  ): ReducerHandlingContextMethods<State, ReducerType>
+  ): ReducerHandlingContextMethods<State>
   /**
    * Provides access to the initial state value given to the slice.
    * If a lazy state initializer was provided, it will be called and a fresh value returned.
@@ -282,7 +279,7 @@ export type ReducerCreator<Type extends RegisteredReducerType> = {
       handle<State>(
         details: ReducerDetails,
         definition: ReducerDefinitionsForType<Type>,
-        context: ReducerHandlingContextMethods<State, Type>
+        context: ReducerHandlingContextMethods<State>
       ): void
     })
 

From db89fb7a52fa4ec3915e82e832150681e601eac1 Mon Sep 17 00:00:00 2001
From: Arya Emami <aryaemami59@yahoo.com>
Date: Sat, 30 Dec 2023 19:56:11 -0600
Subject: [PATCH 032/178] Fix minor type issues inside `BuildCreateSliceConfig`

---
 packages/toolkit/src/createSlice.ts | 15 ++++++---------
 1 file changed, 6 insertions(+), 9 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 24bfced0c4..683968c1f8 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -882,14 +882,10 @@ interface BuildCreateSliceConfig<
 }
 
 export function buildCreateSlice<
-  CreatorMap extends Record<string, RegisteredReducerType> = Partial<
-    Record<'asyncThunk', RegisteredReducerType>
-  >
->({
-  creators: creatorMap = {} as Required<
-    BuildCreateSliceConfig<CreatorMap>['creators']
-  >,
-}: BuildCreateSliceConfig<CreatorMap> = {}) {
+  CreatorMap extends Record<string, RegisteredReducerType> = {}
+>(buildCreateSliceConfig: BuildCreateSliceConfig<CreatorMap> = {}) {
+  const { creators: creatorMap = {} } = buildCreateSliceConfig
+
   const creators: Record<
     string,
     ReducerCreator<RegisteredReducerType>['define']
@@ -906,9 +902,10 @@ export function buildCreateSlice<
     [ReducerType.reducer]: reducerCreator.handle,
     [ReducerType.reducerWithPrepare]: preparedReducerCreator.handle,
   }
+
   for (const [name, creator] of Object.entries<
     ReducerCreator<RegisteredReducerType>
-  >(creatorMap as any)) {
+  >(creatorMap)) {
     if (name === 'reducer' || name === 'preparedReducer') {
       throw new Error('Cannot use reserved creator name: ' + name)
     }

From 58ae8097710c1d06e5750ec3409a0acac311d25b Mon Sep 17 00:00:00 2001
From: Arya Emami <aryaemami59@yahoo.com>
Date: Sat, 30 Dec 2023 20:23:11 -0600
Subject: [PATCH 033/178] Add some type tests for `createSlice`

  - Add type tests to make sure the default `createSlice` does not allow `create.asyncThunk()` but should allow `create.reducer()` and `create.preparedReducer()`
---
 .../toolkit/src/tests/createSlice.typetest.ts | 30 +++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/packages/toolkit/src/tests/createSlice.typetest.ts b/packages/toolkit/src/tests/createSlice.typetest.ts
index c3e613a3c8..5a90de7f63 100644
--- a/packages/toolkit/src/tests/createSlice.typetest.ts
+++ b/packages/toolkit/src/tests/createSlice.typetest.ts
@@ -882,3 +882,33 @@ const value = actionCreators.anyKey
   // @ts-expect-error asyncThunk must be ReducerType.asyncThunk creator
   buildCreateSlice({ creators: { asyncThunk: wrongCreator } })
 }
+
+/**
+ * Test: Default `createSlice` should not allow `create.asyncThunk()`,
+ * but it should allow `create.reducer()` and `create.preparedReducer()`
+ */
+{
+  const sliceWithoutAsyncThunks = createSlice({
+    name: 'counter',
+    initialState: {
+      value: 0,
+      status: 'idle',
+    },
+    reducers: (create) => ({
+      incrementAsync:
+        // @ts-expect-error Default `createSlice` should not allow `create.asyncThunk()`
+        create.asyncThunk(async (amount: number) => amount),
+
+      increment: create.reducer((state) => {
+        state.value += 1
+      }),
+
+      incrementByAmount: create.preparedReducer(
+        (payload: number) => ({ payload }),
+        (state, action: PayloadAction<number>) => {
+          state.value += action.payload
+        }
+      ),
+    }),
+  })
+}

From f39f4c78c23b4db7d9ef9a8d9c6d3dd4e1c9ea8f Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.j.durrant@gmail.com>
Date: Sun, 31 Dec 2023 12:34:47 +0000
Subject: [PATCH 034/178] only assign handler if one exists

---
 packages/toolkit/src/createSlice.ts | 14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 683968c1f8..78e1c117a7 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -883,9 +883,11 @@ interface BuildCreateSliceConfig<
 
 export function buildCreateSlice<
   CreatorMap extends Record<string, RegisteredReducerType> = {}
->(buildCreateSliceConfig: BuildCreateSliceConfig<CreatorMap> = {}) {
-  const { creators: creatorMap = {} } = buildCreateSliceConfig
-
+>({
+  creators: creatorMap = {} as NonNullable<
+    BuildCreateSliceConfig<CreatorMap>['creators']
+  >,
+}: BuildCreateSliceConfig<CreatorMap> = {}) {
   const creators: Record<
     string,
     ReducerCreator<RegisteredReducerType>['define']
@@ -904,7 +906,7 @@ export function buildCreateSlice<
   }
 
   for (const [name, creator] of Object.entries<
-    ReducerCreator<RegisteredReducerType>
+    ReducerCreator<CreatorMap[string]>
   >(creatorMap)) {
     if (name === 'reducer' || name === 'preparedReducer') {
       throw new Error('Cannot use reserved creator name: ' + name)
@@ -916,7 +918,9 @@ export function buildCreateSlice<
       throw new Error('Cannot use reserved creator type: ' + creator.type)
     }
     creators[name] = creator.define
-    handlers[creator.type] = creator.handle
+    if ('handle' in creator) {
+      handlers[creator.type] = creator.handle
+    }
   }
   return function createSlice<
     State,

From d075606ff9c4132b6d1b78736db0617265450d17 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Mon, 1 Jan 2024 00:30:54 +0000
Subject: [PATCH 035/178] Begin writing documentation

---
 docs/api/createSlice.mdx | 149 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 149 insertions(+)

diff --git a/docs/api/createSlice.mdx b/docs/api/createSlice.mdx
index 273cfd2e3c..6664361301 100644
--- a/docs/api/createSlice.mdx
+++ b/docs/api/createSlice.mdx
@@ -600,6 +600,155 @@ const { selectValue } = counterSlice.getSelectors(
 
 :::
 
+## Custom creators
+
+TODO: a blurb introducing the idea of custom creators
+
+###
+
+### Reducer definitions
+
+A reducer definition is an object (or function) with a `_reducerDefinitionType` property indicating which creator should handle it. Other than this property, it is entirely up to you what this definition object can look like.
+
+For example, the `create.preparedReducer` creator uses a definition that looks like `{ prepare, reducer }`.
+
+The callback form of `reducers` should return an object of reducer definitions, by calling creators and nesting the result of each under a key.
+
+```js no-transpile
+reducers: (create) => ({
+  addTodo: create.preparedReducer(
+    (todo) => ({ payload: { id: nanoid(), ...todo } }),
+    (state, action) => {
+      state.push(action.payload)
+    }
+  ),
+})
+// becomes
+const definitions = {
+  addTodo: {
+    _reducerDefinitionType: 'reducerWithPrepare',
+    prepare: (todo) => ({ payload: { id: nanoid(), ...todo } }),
+    reducer: (state, action) => {
+      state.push(action.payload)
+    },
+  },
+}
+```
+
+Typically a creator will return a [single reducer definition](#single-definitions), but it could return an object of [multiple definitions](#multiple-definitions) to be spread into the final object, or [something else entirely](#other)!
+
+### Creator definitions
+
+A creator definition is an object with a `type` property, a `create` property, and an optional `handle` method.
+
+The `type` property should be the same constant used for reducer definitions to be handled by this creator. To avoid collision, we recommend using Symbols for this. It's also used for defining/retrieving types - see [Typescript](#typescript).
+
+```ts no-transpile
+const reducerCreatorType = Symbol()
+
+const reducerCreator: ReducerCreator<typeof reducerCreatorType> = {
+  type: reducerCreatorType,
+  create(reducer) {
+    return {
+      _reducerDefinitionType: reducerCreatorType,
+      reducer,
+    }
+  },
+  handle({ reducerName, type }, definition, context) {
+    const actionCreator = createAction(type)
+    context
+      .addCase(actionCreator, definition.reducer)
+      .exposeAction(reducerName, actionCreator)
+      .exposeCaseReducer(reducerName, reducer)
+  },
+}
+```
+
+#### `create`
+
+The `create` property is the value that will be attached to the `create` object, before it's passed to the `reducers` callback.
+
+Typically, it'll be a function (meaning the `this` value will be the final `create` object when called), but it could be anything - and the function could have additional methods attached.
+
+The actual name the creator will be nested under is taken from the `buildCreateSlice` call. For example, `buildCreateSlice({ creators: { asyncThunk: asyncThunkCreator } })` results in the creator being available as `create.asyncThunk`.
+
+See the [common applications](#common-applications) section for some examples of these.
+
+#### `handle`
+
+The `handle` callback for a creator will be called with any reducer definitions with a matching `_reducerDefinitionType` property.
+
+:::note
+A creator only needs a `handle` callback if it expects to be called with reducer definitions. If it only calls other creators (see [multiple definitions](#multiple-definitions)), it can omit the `handle`.
+:::
+
+It receives three arguments: details about the reducer, the definition, and a `context` object with methods to modify the slice.
+
+The reducer details object has two properties:
+
+- `reducerName` - the key the reducer definition was under (e.g. `addTodo`)
+- `type` - the automatically generated type string for the reducer (e.g. `todos/addTodo`)
+
+The context object includes:
+
+#### `addCase`
+
+The same as [`addCase`](./createReducer#builderaddcase) for `createReducer` and `extraReducers`. Adds a case reducer for a given action type, and can receive an action type string or an action creator with a `.type` property.
+
+```ts no-transpile
+const action = createAction(type)
+context.addCase(action, reducer)
+```
+
+#### `addMatcher`
+
+The same as [`addMatcher`](./createReducer#builderaddmatcher) for `createReducer` and `extraReducers`. Adds a case reducer which will be called when a given matcher returns true.
+
+```ts no-transpile
+const matcher = isAnyOf(action, action2)
+context.addMatcher(matcher, reducer)
+```
+
+#### `exposeAction`
+
+Attaches a value to `slice.actions`. Receives the key to be set under (typically `reducerName`) and the value to be set.
+
+```ts no-transpile
+const action = createAction(type)
+context.exposeAction(reducerName, action)
+```
+
+#### `exposeCaseReducer`
+
+Attaches a value to `slice.caseReducers`. Receives the key to be set under (typically `reducerName`) and the value to be set.
+
+```ts no-transpile
+context.exposeCaseReducer(reducerName, reducer)
+```
+
+#### `getInitialState`
+
+Returns the initial state value for the slice. If a lazy state initializer has been provided, it will be called and a fresh value returned.
+
+```ts no-transpile
+const resetAction = createAction(type)
+const resetReducer = () => context.getInitialState()
+context
+  .addCase(resetAction, resetReducer)
+  .exposeAction(reducerName, resetAction)
+  .exposeCaseReducer(reducerName, resetReducer)
+```
+
+### Typescript
+
+### Common applications
+
+#### Single definitions
+
+#### Multiple definitions
+
+#### Other
+
 ## Examples
 
 ```ts

From fc6737fdeb82aaf56a09b9dbc69e7c98f63db590 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Mon, 1 Jan 2024 00:41:34 +0000
Subject: [PATCH 036/178] minor tweaks

---
 docs/api/createSlice.mdx | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/docs/api/createSlice.mdx b/docs/api/createSlice.mdx
index 6664361301..49814c198b 100644
--- a/docs/api/createSlice.mdx
+++ b/docs/api/createSlice.mdx
@@ -138,7 +138,7 @@ const todosSlice = createSlice({
 
 Alternatively, the `reducers` field can be a callback which receives a "create" object.
 
-The main benefit of this is that you can create [async thunks](./createAsyncThunk) as part of your slice (though for bundle size reasons, you [need a bit of setup for this](#createasyncthunk)). Types are also slightly simplified for prepared reducers.
+The main benefit of this is that you can use [custom creators](#custom-creators) such as [async thunks](./createAsyncThunk) as part of your slice (though for bundle size reasons, you [need a bit of setup for this](#createasyncthunk)). Types are also slightly simplified for prepared reducers.
 
 ```ts title="Creator callback for reducers"
 import { buildCreateSlice, asyncThunkCreator, nanoid } from '@reduxjs/toolkit'
@@ -248,7 +248,7 @@ Creates an async thunk instead of an action creator.
 
 To avoid pulling `createAsyncThunk` into the bundle size of `createSlice` by default, some extra setup is required to use `create.asyncThunk`.
 
-The version of `createSlice` exported from RTK will throw an error if `create.asyncThunk` is called.
+The version of `createSlice` exported from RTK will not include an `asyncThunk` property on the `create` object.
 
 Instead, import `buildCreateSlice` and `asyncThunkCreator`, and create your own version of `createSlice`:
 
@@ -676,7 +676,7 @@ See the [common applications](#common-applications) section for some examples of
 
 #### `handle`
 
-The `handle` callback for a creator will be called with any reducer definitions with a matching `_reducerDefinitionType` property.
+The `handle` callback of a creator will be called for any reducer definitions with a matching `_reducerDefinitionType` property.
 
 :::note
 A creator only needs a `handle` callback if it expects to be called with reducer definitions. If it only calls other creators (see [multiple definitions](#multiple-definitions)), it can omit the `handle`.

From c90f5ba6c340b3f198adb64ca92bb21b080d1eb6 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Mon, 1 Jan 2024 13:27:44 +0000
Subject: [PATCH 037/178] Derive RegisteredReducerType from keys of
 SliceReducerCreators, removing need for ReducerTypes.

---
 packages/toolkit/src/createSlice.ts            | 4 +---
 packages/toolkit/src/index.ts                  | 1 -
 packages/toolkit/src/tests/createSlice.test.ts | 8 --------
 3 files changed, 1 insertion(+), 12 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 78e1c117a7..2b6c183482 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -39,9 +39,7 @@ export enum ReducerType {
   asyncThunk = 'asyncThunk',
 }
 
-export interface ReducerTypes extends Record<ReducerType, true> {}
-
-export type RegisteredReducerType = KeysForValueOfType<ReducerTypes, true>
+export type RegisteredReducerType = keyof SliceReducerCreators
 
 export interface ReducerDefinition<
   T extends RegisteredReducerType = RegisteredReducerType
diff --git a/packages/toolkit/src/index.ts b/packages/toolkit/src/index.ts
index 1d4ea47a27..c18d795ce2 100644
--- a/packages/toolkit/src/index.ts
+++ b/packages/toolkit/src/index.ts
@@ -81,7 +81,6 @@ export type {
   CaseReducerWithPrepare,
   ReducerCreators,
   SliceSelectors,
-  ReducerTypes,
   SliceReducerCreators,
   ReducerDefinition,
   ReducerCreator,
diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index 7d34afa1be..a34628abbd 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -1354,14 +1354,6 @@ interface UndoableOptions {
 }
 
 declare module '@reduxjs/toolkit' {
-  export interface ReducerTypes {
-    [loaderCreatorType]: true
-    [conditionCreatorType]: true
-    [fetchCreatorType]: true
-    [paginationCreatorType]: true
-    [historyMethodsCreatorType]: true
-    [undoableCreatorType]: true
-  }
   export interface SliceReducerCreators<
     State = any,
     CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,

From 219d1bd42f114e774aa2cbb11080fc3aff4bbdcc Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Mon, 1 Jan 2024 13:28:36 +0000
Subject: [PATCH 038/178] rm unused util

---
 packages/toolkit/src/createSlice.ts | 7 +------
 packages/toolkit/src/tsHelpers.ts   | 4 ----
 2 files changed, 1 insertion(+), 10 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 2b6c183482..7a1359207b 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -16,12 +16,7 @@ import type {
 import { createReducer, makeGetInitialState } from './createReducer'
 import type { ActionReducerMapBuilder, TypedActionCreator } from './mapBuilders'
 import { executeReducerBuilderCallback } from './mapBuilders'
-import type {
-  Id,
-  KeysForValueOfType,
-  TypeGuard,
-  UnionToIntersection,
-} from './tsHelpers'
+import type { Id, TypeGuard, UnionToIntersection } from './tsHelpers'
 import type { InjectConfig } from './combineSlices'
 import type {
   AsyncThunk,
diff --git a/packages/toolkit/src/tsHelpers.ts b/packages/toolkit/src/tsHelpers.ts
index d7cdb84412..bd42de858e 100644
--- a/packages/toolkit/src/tsHelpers.ts
+++ b/packages/toolkit/src/tsHelpers.ts
@@ -206,8 +206,4 @@ export type Tail<T extends any[]> = T extends [any, ...infer Tail]
   ? Tail
   : never
 
-export type KeysForValueOfType<T, V> = {
-  [K in keyof T]: T[K] extends V ? K : never
-}[keyof T]
-
 export type UnknownIfNonSpecific<T> = {} extends T ? unknown : T

From 7d33808ca17199316cc17be6bf753b114e69e5bb Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Mon, 1 Jan 2024 13:57:31 +0000
Subject: [PATCH 039/178] find definitions recursively regardless of depth

---
 packages/toolkit/src/createSlice.ts | 37 ++++++++++++++++-------------
 1 file changed, 20 insertions(+), 17 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 7a1359207b..d58f3a78ac 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -249,18 +249,25 @@ interface ReducerDetails {
   type: string
 }
 
-export type ReducerDefinitionsForType<Type extends RegisteredReducerType> = {
-  [T in keyof SliceReducerCreators]-?:
-    | Extract<
-        ReturnType<SliceReducerCreators[Type]['create']>,
-        ReducerDefinition<Type>
-      >
-    | {
-        [K in keyof ReturnType<SliceReducerCreators[T]['create']>]-?: Extract<
-          ReturnType<SliceReducerCreators[T]['create']>[K],
-          ReducerDefinition<Type>
-        >
-      }[keyof ReturnType<SliceReducerCreators[T]['create']>]
+type RecursiveExtractDefinition<
+  Definitions,
+  Type extends RegisteredReducerType
+> =
+  | Extract<Definitions, ReducerDefinition<Type>>
+  | (Definitions extends object
+      ? {
+          [K in keyof Definitions]-?: RecursiveExtractDefinition<
+            Definitions[K],
+            Type
+          >
+        }[keyof Definitions]
+      : never)
+
+type ReducerDefinitionsForType<Type extends RegisteredReducerType> = {
+  [CreatorType in keyof SliceReducerCreators]: RecursiveExtractDefinition<
+    ReturnType<SliceReducerCreators[CreatorType]['create']>,
+    Type
+  >
 }[keyof SliceReducerCreators]
 
 export type ReducerCreator<Type extends RegisteredReducerType> = {
@@ -788,11 +795,7 @@ export const reducerCreator: ReducerCreator<ReducerType.reducer> = {
       } as const
     )
   },
-  handle(
-    { type, reducerName },
-    reducer: CaseReducer<any, PayloadAction<any>>,
-    context
-  ) {
+  handle({ type, reducerName }, reducer, context) {
     context
       .addCase(type, reducer)
       .exposeCaseReducer(reducerName, reducer)

From e3f8e2a5abc092bb27d1972f3b018a355b818137 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Mon, 1 Jan 2024 16:01:35 +0000
Subject: [PATCH 040/178] add default CreatorMap for ReducerCreators type

---
 packages/toolkit/src/createSlice.ts                |  2 +-
 packages/toolkit/src/tests/createSlice.test.ts     | 12 ++++--------
 packages/toolkit/src/tests/createSlice.typetest.ts |  2 +-
 3 files changed, 6 insertions(+), 10 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index d58f3a78ac..b0a446f9c1 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -152,7 +152,7 @@ export interface SliceReducerCreators<
 
 export type ReducerCreators<
   State,
-  CreatorMap extends Record<string, RegisteredReducerType>
+  CreatorMap extends Record<string, RegisteredReducerType> = {}
 > = {
   reducer: SliceReducerCreators<State>[ReducerType.reducer]['create']
   preparedReducer: SliceReducerCreators<State>[ReducerType.reducerWithPrepare]['create']
diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index a34628abbd..2010dd0b7b 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -1418,9 +1418,7 @@ declare module '@reduxjs/toolkit' {
       caseReducers: {}
     }
     [fetchCreatorType]: {
-      create(this: ReducerCreators<State, {}>): State extends FetchState<
-        infer Data
-      >
+      create(this: ReducerCreators<State>): State extends FetchState<infer Data>
         ? {
             start: CaseReducerDefinition<State, PayloadAction>
             success: CaseReducerDefinition<State, PayloadAction<Data>>
@@ -1430,7 +1428,7 @@ declare module '@reduxjs/toolkit' {
       caseReducers: {}
     }
     [paginationCreatorType]: {
-      create(this: ReducerCreators<State, {}>): State extends PaginationState
+      create(this: ReducerCreators<State>): State extends PaginationState
         ? {
             nextPage: CaseReducerDefinition<State, PayloadAction>
             previousPage: CaseReducerDefinition<State, PayloadAction>
@@ -1441,9 +1439,7 @@ declare module '@reduxjs/toolkit' {
       caseReducers: {}
     }
     [historyMethodsCreatorType]: {
-      create(
-        this: ReducerCreators<State, {}>
-      ): State extends HistoryState<unknown>
+      create(this: ReducerCreators<State>): State extends HistoryState<unknown>
         ? {
             undo: CaseReducerDefinition<State, PayloadAction>
             redo: CaseReducerDefinition<State, PayloadAction>
@@ -1474,7 +1470,7 @@ declare module '@reduxjs/toolkit' {
     [undoableCreatorType]: {
       create: {
         <A extends Action & { meta?: UndoableOptions }>(
-          this: ReducerCreators<State, {}>,
+          this: ReducerCreators<State>,
           reducer: CaseReducer<
             State extends HistoryState<infer Data> ? Data : never,
             NoInfer<A>
diff --git a/packages/toolkit/src/tests/createSlice.typetest.ts b/packages/toolkit/src/tests/createSlice.typetest.ts
index 5a90de7f63..0be165beed 100644
--- a/packages/toolkit/src/tests/createSlice.typetest.ts
+++ b/packages/toolkit/src/tests/createSlice.typetest.ts
@@ -822,7 +822,7 @@ const value = actionCreators.anyKey
   }: {
     name: string
     initialState: GenericState<T>
-    reducers: (create: ReducerCreators<GenericState<T>, {}>) => Reducers
+    reducers: (create: ReducerCreators<GenericState<T>>) => Reducers
   }) => {
     return createSlice({
       name,

From c2c948b059ac3a7e4ac178d9c71884bfb2a671a6 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Mon, 1 Jan 2024 16:19:42 +0000
Subject: [PATCH 041/178] add ReducerNamesOfType util

---
 packages/toolkit/src/createSlice.ts           | 21 ++++++----
 packages/toolkit/src/index.ts                 |  1 +
 .../toolkit/src/tests/createSlice.test.ts     | 38 +++++++++----------
 3 files changed, 32 insertions(+), 28 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index b0a446f9c1..0b38bb43bd 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -126,14 +126,10 @@ export interface SliceReducerCreators<
         : never
     }
     caseReducers: {
-      [ReducerName in keyof CaseReducers as CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition<
-        State,
-        any,
-        any,
-        any
-      >
-        ? ReducerName
-        : never]: CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition<
+      [ReducerName in ReducerNamesOfType<
+        CaseReducers,
+        ReducerType.asyncThunk
+      >]: CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition<
         State,
         any,
         any,
@@ -283,6 +279,15 @@ export type ReducerCreator<Type extends RegisteredReducerType> = {
       ): void
     })
 
+export type ReducerNamesOfType<
+  CaseReducers extends SliceCaseReducers<any>,
+  Type extends RegisteredReducerType
+> = {
+  [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends ReducerDefinition<Type>
+    ? ReducerName
+    : never
+}[keyof CaseReducers]
+
 interface InjectIntoConfig<NewReducerPath extends string> extends InjectConfig {
   reducerPath?: NewReducerPath
 }
diff --git a/packages/toolkit/src/index.ts b/packages/toolkit/src/index.ts
index c18d795ce2..85f3e07315 100644
--- a/packages/toolkit/src/index.ts
+++ b/packages/toolkit/src/index.ts
@@ -83,6 +83,7 @@ export type {
   SliceSelectors,
   SliceReducerCreators,
   ReducerDefinition,
+  ReducerNamesOfType,
   ReducerCreator,
   SliceActionType,
   CaseReducerDefinition,
diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index 2010dd0b7b..0fe0ea7fb8 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -11,6 +11,7 @@ import type {
   ReducerCreator,
   ReducerCreators,
   ReducerDefinition,
+  ReducerNamesOfType,
   SliceActionType,
   SliceCaseReducers,
   ThunkAction,
@@ -1364,9 +1365,10 @@ declare module '@reduxjs/toolkit' {
         reducers: Pick<LoaderReducerDefinition<State>, 'ended' | 'started'>
       ): LoaderReducerDefinition<State>
       actions: {
-        [ReducerName in keyof CaseReducers as CaseReducers[ReducerName] extends LoaderReducerDefinition<State>
-          ? ReducerName
-          : never]: (() => ThunkAction<
+        [ReducerName in ReducerNamesOfType<
+          CaseReducers,
+          typeof loaderCreatorType
+        >]: (() => ThunkAction<
           { loaderId: string; end: () => void },
           unknown,
           unknown,
@@ -1383,11 +1385,10 @@ declare module '@reduxjs/toolkit' {
         }
       }
       caseReducers: {
-        [ReducerName in keyof CaseReducers as CaseReducers[ReducerName] extends LoaderReducerDefinition<State>
-          ? ReducerName
-          : never]: Required<
-          Pick<LoaderReducerDefinition<State>, 'ended' | 'started'>
-        >
+        [ReducerName in ReducerNamesOfType<
+          CaseReducers,
+          typeof loaderCreatorType
+        >]: Required<Pick<LoaderReducerDefinition<State>, 'ended' | 'started'>>
       }
     }
     [conditionCreatorType]: {
@@ -1397,11 +1398,10 @@ declare module '@reduxjs/toolkit' {
         makePredicate: (...args: Args) => AnyListenerPredicate<unknown>
       }
       actions: {
-        [ReducerName in keyof CaseReducers as CaseReducers[ReducerName] extends ReducerDefinition<
+        [ReducerName in ReducerNamesOfType<
+          CaseReducers,
           typeof conditionCreatorType
-        >
-          ? ReducerName
-          : never]: CaseReducers[ReducerName] extends {
+        >]: CaseReducers[ReducerName] extends {
           makePredicate: (...args: infer Args) => AnyListenerPredicate<unknown>
         }
           ? (
@@ -1449,20 +1449,18 @@ declare module '@reduxjs/toolkit' {
           }
         : never
       actions: {
-        [ReducerName in keyof CaseReducers as CaseReducers[ReducerName] extends ReducerDefinition<
+        [ReducerName in ReducerNamesOfType<
+          CaseReducers,
           typeof historyMethodsCreatorType
-        >
-          ? ReducerName
-          : never]: CaseReducers[ReducerName] extends { type: 'reset' }
+        >]: CaseReducers[ReducerName] extends { type: 'reset' }
           ? PayloadActionCreator<void, SliceActionType<Name, ReducerName>>
           : never
       }
       caseReducers: {
-        [ReducerName in keyof CaseReducers as CaseReducers[ReducerName] extends ReducerDefinition<
+        [ReducerName in ReducerNamesOfType<
+          CaseReducers,
           typeof historyMethodsCreatorType
-        >
-          ? ReducerName
-          : never]: CaseReducers[ReducerName] extends { type: 'reset' }
+        >]: CaseReducers[ReducerName] extends { type: 'reset' }
           ? CaseReducer<State, PayloadAction>
           : never
       }

From 06d3ffcaeca301e93dcb3d19fb51b66f264b0bf8 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Mon, 1 Jan 2024 16:40:15 +0000
Subject: [PATCH 042/178] add Typescript section

---
 docs/api/createSlice.mdx | 168 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 165 insertions(+), 3 deletions(-)

diff --git a/docs/api/createSlice.mdx b/docs/api/createSlice.mdx
index 49814c198b..10ea247c34 100644
--- a/docs/api/createSlice.mdx
+++ b/docs/api/createSlice.mdx
@@ -604,8 +604,6 @@ const { selectValue } = counterSlice.getSelectors(
 
 TODO: a blurb introducing the idea of custom creators
 
-###
-
 ### Reducer definitions
 
 A reducer definition is an object (or function) with a `_reducerDefinitionType` property indicating which creator should handle it. Other than this property, it is entirely up to you what this definition object can look like.
@@ -679,7 +677,7 @@ See the [common applications](#common-applications) section for some examples of
 The `handle` callback of a creator will be called for any reducer definitions with a matching `_reducerDefinitionType` property.
 
 :::note
-A creator only needs a `handle` callback if it expects to be called with reducer definitions. If it only calls other creators (see [multiple definitions](#multiple-definitions)), it can omit the `handle`.
+A creator only needs a `handle` callback if it expects to be called with reducer definitions. If it only calls other creators (see [Using `this` to access other creators](#create-1)), it can omit the `handle`.
 :::
 
 It receives three arguments: details about the reducer, the definition, and a `context` object with methods to modify the slice.
@@ -741,6 +739,170 @@ context
 
 ### Typescript
 
+The Typescript system for custom slice creators uses a "creator registry" system similar to the module system for [RTK Query](/rtk-query/usage/customizing-create-api#creating-your-own-module).
+
+Creators are registered by using module augmentation to add a new key (their unique `type`) to the `SliceReducerCreators` interface. The interface receives three type parameters (`State`, `CaseReducers` and `Name`), and each entry should have three keys (`create`, `actions`, and `caseReducers`).
+
+```ts no-transpile
+const reducerCreatorType = Symbol()
+
+declare module '@reduxjs/toolkit' {
+  export interface SliceReducerCreators<
+    State = any,
+    CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
+    Name extends string = string
+  > {
+    [reducerCreatorType]: {
+      create(): ReducerDefinition<typeof reducerCreatorType>
+      actions: {}
+      caseReducers: {}
+    }
+  }
+}
+```
+
+:::danger
+
+All three keys must be provided, even if the creator only returns definitions for other creators. If the creator doesn't expose any actions/case reducers itself, leave those keys as `{}`.
+
+:::
+
+#### `create`
+
+The type of the `create` property of the creator definition. Typically a function signature, but this isn't required.
+
+:::tip Using `this` to access other creators
+
+Assuming the creator is called as `create.yourCreator()`, the `this` value for the function is the create object - meaning you can call other creators on the same object.
+
+However, this should be specifically included in the function signature, so Typescript can warn if called with an incorrect context (for example, if the user destructures from the `create` value).
+
+```ts no-transpile
+const batchedCreatorType = Symbol()
+
+declare module '@reduxjs/toolkit' {
+  export interface SliceReducerCreators<
+    State = any,
+    CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
+    Name extends string = string
+  > {
+    [batchedCreatorType]: {
+      // highlight-start
+      create<Payload>(
+        this: ReducerCreators<State, {}>,
+        reducer: CaseReducer<State, PayloadAction<Payload>>
+      ): PreparedCaseReducerDefinition<
+        State,
+        (payload: Payload) => { payload: Payload; meta: unknown }
+      >
+      // highlight-end
+      actions: {}
+      caseReducers: {}
+    }
+  }
+}
+
+const batchedCreator: ReducerCreator<typeof batchedCreatorType> = {
+  type: batchedCreatorType,
+  create(reducer) {
+    return this.preparedReducer(prepareAutoBatched(), reducer)
+  },
+}
+```
+
+The second argument to the `ReducerCreators` type is a map from creator names to types, which you should supply if you're expecting to use any custom creators (anything other than `reducer` and `preparedReducer`) within your own creator. For example, `ReducerCreators<State, { asyncThunk: typeof asyncThunkCreator.type }>` would allow you to call `this.asyncThunk`.
+
+:::
+
+#### `actions`
+
+The actions property will typically be a [mapped type](https://www.typescriptlang.org/docs/handbook/2/mapped-types.html) over the `CaseReducers` type parameter, returning what the creator's `handle` would expose when given that definition.
+
+The `ReducerNamesOfType` utility is exported to easily filter down to reducers that would be passed to the `handle` callback.
+
+For example, with (a simplified version of) the `asyncThunk` creator:
+
+```ts no-transpile
+const asyncThunkCreatorType = Symbol()
+
+declare module '@reduxjs/toolkit' {
+  export interface SliceReducerCreators<
+    State = any,
+    CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
+    Name extends string = string
+  > {
+    [asyncThunkCreatorType]: {
+      create<ThunkArg, Returned>(
+        payloadCreator: AsyncThunkPayloadCreator<ThunkArg, Returned>
+      ): AsyncThunkReducerDefinition<State, ThunkArg, Returned>
+      // highlight-start
+      actions: {
+        [ReducerName in ReducerNamesOfType<
+          CaseReducers,
+          typeof asyncThunkCreatorType
+        >]: CaseReducers[ReducerName] extends AsyncThunkReducerDefinition<
+          State,
+          infer ThunkArg,
+          infer Returned
+        >
+          ? AsyncThunk<ThunkArg, Returned>
+          : never
+      }
+      // highlight-end
+      caseReducers: {}
+    }
+  }
+}
+```
+
+The `actions` returned from all the creators when passed the `CaseReducers` definitions are all intersected to create the final type for `slice.actions`.
+
+#### `caseReducers`
+
+Similar to `actions`, except for `slice.caseReducers`.
+
+For example, with the `preparedReducer` creator:
+
+```ts no-transpile
+const preparedReducerType = Symbol()
+
+declare module '@reduxjs/toolkit' {
+  export interface SliceReducerCreators<
+    State = any,
+    CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
+    Name extends string = string
+  > {
+    [preparedReducerType]: {
+      create<Prepare extends PrepareAction<any>>(
+        prepare: Prepare,
+        caseReducer: CaseReducer<State, ActionForPrepare<Prepare>>
+      ): PreparedCaseReducerDefinition<State, Prepare>
+      actions: {
+        [ReducerName in ReducerNamesOfType<
+          CaseReducers,
+          typeof preparedReducerType
+        >]: CaseReducers[ReducerName] extends { prepare: any }
+          ? ActionCreatorForCaseReducerWithPrepare<
+              CaseReducers[ReducerName],
+              SliceActionType<Name, ReducerName>
+            >
+          : never
+      }
+      // highlight-start
+      caseReducers: {
+        [ReducerName in ReducerNamesOfType<
+          CaseReducers,
+          typeof preparedReducerType
+        >]: CaseReducers[ReducerName] extends { reducer: infer Reducer }
+          ? Reducer
+          : never
+      }
+      // highlight-end
+    }
+  }
+}
+```
+
 ### Common applications
 
 #### Single definitions

From e641fb5c12c7b4071355d04705cadaa04cf27edc Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Mon, 1 Jan 2024 16:40:46 +0000
Subject: [PATCH 043/178] use ReducerNamesOfType more

---
 packages/toolkit/src/createSlice.ts | 12 ++++--------
 1 file changed, 4 insertions(+), 8 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 0b38bb43bd..90fd4e2d99 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -109,15 +109,11 @@ export interface SliceReducerCreators<
   [ReducerType.asyncThunk]: {
     create: AsyncThunkCreator<State>
     actions: {
-      [ReducerName in keyof CaseReducers as CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition<
+      [ReducerName in ReducerNamesOfType<
+        CaseReducers,
+        ReducerType.asyncThunk
+      >]: CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition<
         State,
-        any,
-        any,
-        any
-      >
-        ? ReducerName
-        : never]: CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition<
-        any,
         infer ThunkArg,
         infer Returned,
         infer ThunkApiConfig

From 2b936b84f96eb07730b248bbd3b60fc52868e88e Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.j.durrant@gmail.com>
Date: Mon, 1 Jan 2024 20:25:20 +0000
Subject: [PATCH 044/178] Batched creator test

---
 packages/toolkit/src/createSlice.ts           |  2 +-
 .../toolkit/src/tests/createSlice.test.ts     | 52 +++++++++++++++++++
 2 files changed, 53 insertions(+), 1 deletion(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 90fd4e2d99..2db3512c56 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -460,7 +460,7 @@ export interface CreateSliceOptions<
    */
   reducers:
     | ValidateSliceCaseReducers<State, CR>
-    | ((creators: ReducerCreators<State, CreatorMap>) => CR)
+    | ((create: ReducerCreators<State, CreatorMap>) => CR)
 
   /**
    * A callback that receives a *builder* object to define
diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index 0fe0ea7fb8..b836741193 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -8,6 +8,7 @@ import type {
   CaseReducerDefinition,
   PayloadAction,
   PayloadActionCreator,
+  PreparedCaseReducerDefinition,
   ReducerCreator,
   ReducerCreators,
   ReducerDefinition,
@@ -33,6 +34,8 @@ import {
   createNextState,
   reducerCreator,
   ReducerType,
+  prepareAutoBatched,
+  SHOULD_AUTOBATCH,
 } from '@reduxjs/toolkit'
 import {
   mockConsole,
@@ -51,6 +54,7 @@ const fetchCreatorType = Symbol()
 const paginationCreatorType = Symbol()
 const historyMethodsCreatorType = Symbol()
 const undoableCreatorType = Symbol()
+const batchedCreatorType = Symbol()
 
 describe('createSlice', () => {
   let restore: () => void
@@ -1306,6 +1310,32 @@ describe('createSlice', () => {
         store.dispatch(reset())
         expect(selectValue(store.getState())).toBe(1)
       })
+      test('batchable', () => {
+        const batchedCreator: ReducerCreator<typeof batchedCreatorType> = {
+          type: batchedCreatorType,
+          define(this: ReducerCreators<any>, reducer: CaseReducer<any, any>) {
+            return this.preparedReducer(prepareAutoBatched(), reducer) as any
+          },
+        }
+        const createBatchSlice = buildCreateSlice({
+          creators: { batchedReducer: batchedCreator },
+        })
+        const counterSlice = createBatchSlice({
+          name: 'counter',
+          initialState: { value: 0 },
+          reducers: (create) => ({
+            increment: create.batchedReducer((state) => {
+              state.value++
+            }),
+            incrementBy: create.batchedReducer<number>((state, { payload }) => {
+              state.value += payload
+            }),
+          }),
+        })
+        const { increment, incrementBy } = counterSlice.actions
+        expect(increment().meta).toEqual({ [SHOULD_AUTOBATCH]: true })
+        expect(incrementBy(1).meta).toEqual({ [SHOULD_AUTOBATCH]: true })
+      })
       test.skip('creators can discourage their use if state is incompatible (types only)', () => {
         const createFetchSlice = buildCreateSlice({
           creators: { fetchReducers: fetchCreator },
@@ -1489,5 +1519,27 @@ declare module '@reduxjs/toolkit' {
       actions: {}
       caseReducers: {}
     }
+    [batchedCreatorType]: {
+      create(
+        this: ReducerCreators<State>,
+        reducer: CaseReducer<State, PayloadAction>
+      ): PreparedCaseReducerDefinition<
+        State,
+        () => { payload: undefined; meta: unknown }
+      >
+      create<Payload>(
+        this: ReducerCreators<State>,
+        reducer: CaseReducer<State, PayloadAction<Payload>>
+      ): PreparedCaseReducerDefinition<
+        State,
+        IfMaybeUndefined<
+          Payload,
+          (payload?: Payload) => { payload: Payload; meta: unknown },
+          (payload: Payload) => { payload: Payload; meta: unknown }
+        >
+      >
+      actions: {}
+      caseReducers: {}
+    }
   }
 }

From 5173d2f92742f35bf4b1774bd1f3ae475fc5f286 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.j.durrant@gmail.com>
Date: Mon, 1 Jan 2024 20:26:06 +0000
Subject: [PATCH 045/178] define -> create

---
 packages/toolkit/src/createSlice.ts            | 16 ++++++++--------
 packages/toolkit/src/tests/createSlice.test.ts | 16 ++++++++--------
 2 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 2db3512c56..41363849d3 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -264,7 +264,7 @@ type ReducerDefinitionsForType<Type extends RegisteredReducerType> = {
 
 export type ReducerCreator<Type extends RegisteredReducerType> = {
   type: Type
-  define: SliceReducerCreators[Type]['create']
+  create: SliceReducerCreators[Type]['create']
 } & (ReducerDefinitionsForType<Type> extends never
   ? {}
   : {
@@ -782,7 +782,7 @@ function getType(slice: string, actionKey: string): string {
 
 export const reducerCreator: ReducerCreator<ReducerType.reducer> = {
   type: ReducerType.reducer,
-  define(caseReducer: CaseReducer<any, any>) {
+  create(caseReducer: CaseReducer<any, any>) {
     return Object.assign(
       {
         // hack so the wrapping function has the same name as the original
@@ -807,7 +807,7 @@ export const reducerCreator: ReducerCreator<ReducerType.reducer> = {
 export const preparedReducerCreator: ReducerCreator<ReducerType.reducerWithPrepare> =
   {
     type: ReducerType.reducerWithPrepare,
-    define(prepare, reducer) {
+    create(prepare, reducer) {
       return {
         _reducerDefinitionType: ReducerType.reducerWithPrepare,
         prepare,
@@ -824,7 +824,7 @@ export const preparedReducerCreator: ReducerCreator<ReducerType.reducerWithPrepa
 
 export const asyncThunkCreator: ReducerCreator<ReducerType.asyncThunk> = {
   type: ReducerType.asyncThunk,
-  define: /* @__PURE__ */ (() => {
+  create: /* @__PURE__ */ (() => {
     function asyncThunk(
       payloadCreator: AsyncThunkPayloadCreator<any, any>,
       config: AsyncThunkSliceReducerConfig<any, any>
@@ -887,10 +887,10 @@ export function buildCreateSlice<
 }: BuildCreateSliceConfig<CreatorMap> = {}) {
   const creators: Record<
     string,
-    ReducerCreator<RegisteredReducerType>['define']
+    ReducerCreator<RegisteredReducerType>['create']
   > = {
-    reducer: reducerCreator.define,
-    preparedReducer: preparedReducerCreator.define,
+    reducer: reducerCreator.create,
+    preparedReducer: preparedReducerCreator.create,
   }
   const handlers: Partial<
     Record<
@@ -914,7 +914,7 @@ export function buildCreateSlice<
     ) {
       throw new Error('Cannot use reserved creator type: ' + creator.type)
     }
-    creators[name] = creator.define
+    creators[name] = creator.create
     if ('handle' in creator) {
       handlers[creator.type] = creator.handle
     }
diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index b836741193..ddfd20ac6d 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -874,7 +874,7 @@ describe('createSlice', () => {
   describe('custom slice reducer creators', () => {
     const loaderCreator: ReducerCreator<typeof loaderCreatorType> = {
       type: loaderCreatorType,
-      define(reducers) {
+      create(reducers) {
         return {
           _reducerDefinitionType: loaderCreatorType,
           ...reducers,
@@ -969,7 +969,7 @@ describe('createSlice', () => {
 
     const conditionCreator: ReducerCreator<typeof conditionCreatorType> = {
       type: conditionCreatorType,
-      define(makePredicate) {
+      create(makePredicate) {
         return { _reducerDefinitionType: conditionCreatorType, makePredicate }
       },
       handle({ reducerName, type }, { makePredicate }, context) {
@@ -1075,7 +1075,7 @@ describe('createSlice', () => {
     describe('creators can return multiple definitions to be spread', () => {
       const fetchCreator: ReducerCreator<typeof fetchCreatorType> = {
         type: fetchCreatorType,
-        define() {
+        create() {
           return {
             start: this.reducer((state: FetchState<unknown>) => {
               state.status = 'loading'
@@ -1113,7 +1113,7 @@ describe('createSlice', () => {
         const paginationCreator: ReducerCreator<typeof paginationCreatorType> =
           {
             type: paginationCreatorType,
-            define() {
+            create() {
               return {
                 nextPage: this.reducer((state: PaginationState) => {
                   state.page++
@@ -1175,7 +1175,7 @@ describe('createSlice', () => {
           typeof historyMethodsCreatorType
         > = {
           type: historyMethodsCreatorType,
-          define() {
+          create() {
             return {
               undo: this.reducer((state: HistoryState<unknown>) => {
                 const historyEntry = state.past.pop()
@@ -1235,7 +1235,7 @@ describe('createSlice', () => {
             }
             return finalState
           }
-        } as ReducerCreator<typeof undoableCreatorType>['define']
+        } as ReducerCreator<typeof undoableCreatorType>['create']
 
         makeUndoable.withoutPayload = () => (options) => ({
           payload: undefined,
@@ -1249,7 +1249,7 @@ describe('createSlice', () => {
 
         const undoableCreator: ReducerCreator<typeof undoableCreatorType> = {
           type: undoableCreatorType,
-          define: makeUndoable,
+          create: makeUndoable,
         }
 
         const createHistorySlice = buildCreateSlice({
@@ -1313,7 +1313,7 @@ describe('createSlice', () => {
       test('batchable', () => {
         const batchedCreator: ReducerCreator<typeof batchedCreatorType> = {
           type: batchedCreatorType,
-          define(this: ReducerCreators<any>, reducer: CaseReducer<any, any>) {
+          create(this: ReducerCreators<any>, reducer: CaseReducer<any, any>) {
             return this.preparedReducer(prepareAutoBatched(), reducer) as any
           },
         }

From 98e1eb5eb9c015b62f49d5e1abc17a89ced07829 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.durrant@marketdojo.com>
Date: Tue, 2 Jan 2024 17:39:50 +0000
Subject: [PATCH 046/178] add "single definition" example

---
 docs/api/createSlice.mdx | 281 ++++++++++++++++++++++++++++++---------
 1 file changed, 218 insertions(+), 63 deletions(-)

diff --git a/docs/api/createSlice.mdx b/docs/api/createSlice.mdx
index 10ea247c34..b96c7662b0 100644
--- a/docs/api/createSlice.mdx
+++ b/docs/api/createSlice.mdx
@@ -600,6 +600,73 @@ const { selectValue } = counterSlice.getSelectors(
 
 :::
 
+## Examples
+
+```ts
+import { createSlice, createAction, configureStore } from '@reduxjs/toolkit'
+import type { PayloadAction } from '@reduxjs/toolkit'
+import { combineReducers } from 'redux'
+
+const incrementBy = createAction<number>('incrementBy')
+const decrementBy = createAction<number>('decrementBy')
+
+const counter = createSlice({
+  name: 'counter',
+  initialState: 0 as number,
+  reducers: {
+    increment: (state) => state + 1,
+    decrement: (state) => state - 1,
+    multiply: {
+      reducer: (state, action: PayloadAction<number>) => state * action.payload,
+      prepare: (value?: number) => ({ payload: value || 2 }), // fallback if the payload is a falsy value
+    },
+  },
+  extraReducers: (builder) => {
+    builder.addCase(incrementBy, (state, action) => {
+      return state + action.payload
+    })
+    builder.addCase(decrementBy, (state, action) => {
+      return state - action.payload
+    })
+  },
+})
+
+const user = createSlice({
+  name: 'user',
+  initialState: { name: '', age: 20 },
+  reducers: {
+    setUserName: (state, action) => {
+      state.name = action.payload // mutate the state all you want with immer
+    },
+  },
+  extraReducers: (builder) => {
+    builder.addCase(counter.actions.increment, (state, action) => {
+      state.age += 1
+    })
+  },
+})
+
+const store = configureStore({
+  reducer: {
+    counter: counter.reducer,
+    user: user.reducer,
+  },
+})
+
+store.dispatch(counter.actions.increment())
+// -> { counter: 1, user: {name : '', age: 21} }
+store.dispatch(counter.actions.increment())
+// -> { counter: 2, user: {name: '', age: 22} }
+store.dispatch(counter.actions.multiply(3))
+// -> { counter: 6, user: {name: '', age: 22} }
+store.dispatch(counter.actions.multiply())
+// -> { counter: 12, user: {name: '', age: 22} }
+console.log(counter.actions.decrement.type)
+// -> "counter/decrement"
+store.dispatch(user.actions.setUserName('eric'))
+// -> { counter: 12, user: { name: 'eric', age: 22} }
+```
+
 ## Custom creators
 
 TODO: a blurb introducing the idea of custom creators
@@ -653,9 +720,10 @@ const reducerCreator: ReducerCreator<typeof reducerCreatorType> = {
     }
   },
   handle({ reducerName, type }, definition, context) {
+    const { reducer } = definition
     const actionCreator = createAction(type)
     context
-      .addCase(actionCreator, definition.reducer)
+      .addCase(actionCreator, reducer)
       .exposeAction(reducerName, actionCreator)
       .exposeCaseReducer(reducerName, reducer)
   },
@@ -670,7 +738,7 @@ Typically, it'll be a function (meaning the `this` value will be the final `crea
 
 The actual name the creator will be nested under is taken from the `buildCreateSlice` call. For example, `buildCreateSlice({ creators: { asyncThunk: asyncThunkCreator } })` results in the creator being available as `create.asyncThunk`.
 
-See the [common applications](#common-applications) section for some examples of these.
+See the [Further examples](#further-examples) section for some examples of these.
 
 #### `handle`
 
@@ -773,7 +841,7 @@ The type of the `create` property of the creator definition. Typically a functio
 
 :::tip Using `this` to access other creators
 
-Assuming the creator is called as `create.yourCreator()`, the `this` value for the function is the create object - meaning you can call other creators on the same object.
+Assuming the creator is called as `create.yourCreator()`, the `this` value for the function is the `create` object - meaning you can call other creators on the same object.
 
 However, this should be specifically included in the function signature, so Typescript can warn if called with an incorrect context (for example, if the user destructures from the `create` value).
 
@@ -787,15 +855,14 @@ declare module '@reduxjs/toolkit' {
     Name extends string = string
   > {
     [batchedCreatorType]: {
-      // highlight-start
       create<Payload>(
+        // highlight-next-line
         this: ReducerCreators<State, {}>,
         reducer: CaseReducer<State, PayloadAction<Payload>>
       ): PreparedCaseReducerDefinition<
         State,
         (payload: Payload) => { payload: Payload; meta: unknown }
       >
-      // highlight-end
       actions: {}
       caseReducers: {}
     }
@@ -903,77 +970,165 @@ declare module '@reduxjs/toolkit' {
 }
 ```
 
-### Common applications
+### Further examples
+
+TODO: blurb
 
 #### Single definitions
 
-#### Multiple definitions
+Commonly, a creator will return a single reducer definition, to be handled by either itself or another creator.
 
-#### Other
+One example would be reusable toast logic; you could have a reducer creator that makes a thunk creator. That thunk would dispatch an "show" action immediately when called, and then dispatch a second "hide" action after a given amount of time.
 
-## Examples
+```ts no-transpile
+// create the unique type
+const toastCreatorType = Symbol()
 
-```ts
-import { createSlice, createAction, configureStore } from '@reduxjs/toolkit'
-import type { PayloadAction } from '@reduxjs/toolkit'
-import { combineReducers } from 'redux'
+interface Toast {
+  message: string
+}
 
-const incrementBy = createAction<number>('incrementBy')
-const decrementBy = createAction<number>('decrementBy')
+interface ToastReducerConfig<State> {
+  shown?: CaseReducer<State, PayloadAction<Toast, string, { id: string }>>
+  hidden?: CaseReducer<State, PayloadAction<undefined, string, { id: string }>>
+}
 
-const counter = createSlice({
-  name: 'counter',
-  initialState: 0 as number,
-  reducers: {
-    increment: (state) => state + 1,
-    decrement: (state) => state - 1,
-    multiply: {
-      reducer: (state, action: PayloadAction<number>) => state * action.payload,
-      prepare: (value?: number) => ({ payload: value || 2 }), // fallback if the payload is a falsy value
-    },
-  },
-  extraReducers: (builder) => {
-    builder.addCase(incrementBy, (state, action) => {
-      return state + action.payload
-    })
-    builder.addCase(decrementBy, (state, action) => {
-      return state - action.payload
-    })
-  },
-})
+interface ToastThunkCreator<
+  SliceName extends string,
+  ReducerName extends string
+> {
+  (toast: Toast, timeout?: number): ThunkAction<
+    { hide(): void },
+    unknown,
+    unknown,
+    UnknownAction
+  >
+  shown: PayloadActionCreator<
+    Toast,
+    `${SliceActionType<SliceName, ReducerName>}/shown`,
+    (toast: Toast, id: string) => { payload: Toast; meta: { id: string } }
+  >
+  hidden: PayloadActionCreator<
+    void,
+    `${SliceActionType<SliceName, ReducerName>}/hidden`,
+    (id: string) => { payload: undefined; meta: { id: string } }
+  >
+}
 
-const user = createSlice({
-  name: 'user',
-  initialState: { name: '', age: 20 },
-  reducers: {
-    setUserName: (state, action) => {
-      state.name = action.payload // mutate the state all you want with immer
-    },
+// register the creator types
+declare module '@reduxjs/toolkit' {
+  export interface SliceReducerCreators<
+    State = any,
+    CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
+    Name extends string = string
+  > {
+    [toastCreatorType]: {
+      create(
+        config: ToastReducerConfig<State>
+      ): ToastReducerConfig<State> & ReducerDefinition<typeof toastCreatorType>
+      actions: {
+        [ReducerName in ReducerNamesOfType<
+          typeof toastCreatorType
+        >]: ToastThunkCreator<Name, ReducerName>
+      }
+      caseReducers: {
+        [ReducerName in ReducerNamesOfType<typeof toastCreatorType>]: Required<
+          ToastReducerConfig<State>
+        >
+      }
+    }
+  }
+}
+
+// define the creator
+const toastCreator: ReducerCreator<typeof toastCreatorType> = {
+  type: toastCreatorType,
+  // return the reducer definition
+  create(config) {
+    return {
+      _reducerDefinitionType: toastCreatorType,
+      ...config,
+    }
   },
-  extraReducers: (builder) => {
-    builder.addCase(counter.actions.increment, (state, action) => {
-      state.age += 1
-    })
+  // handle the reducer definition
+  handle({ reducerName, type }, definition, context) {
+    // make the action creators
+    const shown = createAction(type + '/shown', (toast: Toast, id: string) => ({
+      payload: toast,
+      meta: { id },
+    }))
+    const hidden = createAction(type + '/hidden', (id: string) => ({
+      payload: undefined,
+      meta: { id },
+    }))
+    // make the thunk creator
+    function thunkCreator(
+      toast: Toast,
+      timeout = 300
+    ): ThunkAction<{ hide(): void }, unknown, unknown, UnknownAction> {
+      return (dispatch, getState) => {
+        const id = nanoid()
+        dispatch(shown(toast, id))
+        const timeout = setTimeout(() => dispatch(hidden(id)), 300)
+        return {
+          hide() {
+            clearTimeout(timeout)
+            dispatch(hidden(id))
+          },
+        }
+      }
+    }
+    // attach the action creators to the thunk creator
+    Object.assign(thunkCreator, { shown, hidden })
+
+    // add any case reducers passed in the config
+    if (definition.shown) {
+      context.addCase(shown, definition.shown)
+    }
+    if (definition.hidden) {
+      context.addCase(hidden, definition.hidden)
+    }
+
+    // expose the thunk creator as `slice.actions[reducerName]` and the case reducers as `slice.caseReducers[reducerName]["shown" | "hidden"]`
+    context
+      .exposeAction(reducerName, thunkCreator)
+      .exposeCaseReducer(reducerName, {
+        shown: definition.shown || noop,
+        hidden: definition.hidden || noop,
+      })
   },
+}
+
+function noop() {}
+
+// build the `createSlice` function
+const createToastSlice = buildCreateSlice({
+  creators: { toaster: toastCreator },
 })
 
-const store = configureStore({
-  reducer: {
-    counter: counter.reducer,
-    user: user.reducer,
-  },
+const toastSlice = createToastSlice({
+  name: 'toast',
+  initialState: {} as Record<string, Toast>,
+  reducers: (create) => ({
+    // call creator to get definition, and save it to a key
+    showToast: create.toaster({
+      shown(state, action) {
+        state[action.meta.id] = action.payload
+      },
+      hidden(state, action) {
+        delete state[action.meta.id]
+      },
+    }),
+  }),
 })
 
-store.dispatch(counter.actions.increment())
-// -> { counter: 1, user: {name : '', age: 21} }
-store.dispatch(counter.actions.increment())
-// -> { counter: 2, user: {name: '', age: 22} }
-store.dispatch(counter.actions.multiply(3))
-// -> { counter: 6, user: {name: '', age: 22} }
-store.dispatch(counter.actions.multiply())
-// -> { counter: 12, user: {name: '', age: 22} }
-console.log(counter.actions.decrement.type)
-// -> "counter/decrement"
-store.dispatch(user.actions.setUserName('eric'))
-// -> { counter: 12, user: { name: 'eric', age: 22} }
+// showToast is the thunk creator from above
+const { showToast } = toastSlice.actions
+
+// case reducers and action creators are available where we put them
+toastSlice.caseReducers.showToast.hidden({}, showToast.hidden('id'))
 ```
+
+#### Multiple definitions
+
+#### Other

From aa92ced98b44228776e29a43bebe76f2e2c0ac95 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Tue, 2 Jan 2024 19:37:12 +0000
Subject: [PATCH 047/178] add pagination example

---
 docs/api/createSlice.mdx | 71 ++++++++++++++++++++++++++++++++++++++--
 1 file changed, 69 insertions(+), 2 deletions(-)

diff --git a/docs/api/createSlice.mdx b/docs/api/createSlice.mdx
index b96c7662b0..8104a8eddf 100644
--- a/docs/api/createSlice.mdx
+++ b/docs/api/createSlice.mdx
@@ -1069,10 +1069,10 @@ const toastCreator: ReducerCreator<typeof toastCreatorType> = {
       return (dispatch, getState) => {
         const id = nanoid()
         dispatch(shown(toast, id))
-        const timeout = setTimeout(() => dispatch(hidden(id)), 300)
+        const timeoutId = setTimeout(() => dispatch(hidden(id)), timeout)
         return {
           hide() {
-            clearTimeout(timeout)
+            clearTimeout(timeoutId)
             dispatch(hidden(id))
           },
         }
@@ -1131,4 +1131,71 @@ toastSlice.caseReducers.showToast.hidden({}, showToast.hidden('id'))
 
 #### Multiple definitions
 
+A creator could also return multiple definitions, which would then be spread into the final definitions object. This is a more composable alternative to the [wrapping createSlice](usage/usage-with-typescript#wrapping-createslice) approach, as you could call multiple creators as needed.
+
+One example could be returning some pagination related reducers.
+
+```ts no-transpile
+const paginationCreatorType = Symbol()
+
+interface PaginationState {
+  page: number
+}
+
+declare module '@reduxjs/toolkit' {
+  export interface SliceReducerCreators<
+    State = any,
+    CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
+    Name extends string = string
+  > {
+    [paginationCreatorType]: {
+      // make sure the creator is only called when state is compatible
+      create(this: ReducerCreators<State>): State extends PaginationState
+        ? {
+            prevPage: CaseReducerDefinition<State, PayloadAction>
+            nextPage: CaseReducerDefinition<State, PayloadAction>
+            goToPage: CaseReducerDefinition<State, PayloadAction<number>>
+          }
+        : never
+      actions: {}
+      caseReducers: {}
+    }
+  }
+}
+
+const paginationCreator: ReducerCreator<typeof paginationCreatorType> = {
+  type: paginationCreatorType,
+  create() {
+    return {
+      prevPage: this.reducer((state: PaginationState) => {
+        state.page--
+      }),
+      nextPage: this.reducer((state: PaginationState) => {
+        state.page++
+      }),
+      goToPage: this.reducer<number>((state: PaginationState, action) => {
+        state.page = action.payload
+      }),
+    }
+  },
+}
+
+const createPaginationSlice = buildCreateSlice({
+  creators: { paginationReducers: paginationCreator },
+})
+
+const paginationSlice = createPaginationSlice({
+  name: 'pagination',
+  initialState: { page: 0, loading: false },
+  reducers: (create) => ({
+    ...create.paginationReducers(),
+    toggleLoading: create.reducer((state) => {
+      state.loading = !state.loading
+    }),
+  }),
+})
+
+const { prevPage, nextPage, goToPage, toggleLoading } = paginationSlice.actions
+```
+
 #### Other

From 02a4008a0cddb1b5e34542aef99074e3f574bf28 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Tue, 2 Jan 2024 22:07:52 +0000
Subject: [PATCH 048/178] Add "other" example

---
 docs/api/createSlice.mdx                      | 218 +++++++++++++++++-
 .../toolkit/src/tests/createSlice.test.ts     |   4 +-
 2 files changed, 218 insertions(+), 4 deletions(-)

diff --git a/docs/api/createSlice.mdx b/docs/api/createSlice.mdx
index 8104a8eddf..0316422897 100644
--- a/docs/api/createSlice.mdx
+++ b/docs/api/createSlice.mdx
@@ -1131,7 +1131,7 @@ toastSlice.caseReducers.showToast.hidden({}, showToast.hidden('id'))
 
 #### Multiple definitions
 
-A creator could also return multiple definitions, which would then be spread into the final definitions object. This is a more composable alternative to the [wrapping createSlice](usage/usage-with-typescript#wrapping-createslice) approach, as you could call multiple creators as needed.
+A creator could also return multiple definitions, which would then be spread into the final definitions object. This is a more composable alternative to the [wrapping `createSlice`](usage/usage-with-typescript#wrapping-createslice) approach, as you could call multiple creators as needed.
 
 One example could be returning some pagination related reducers.
 
@@ -1198,4 +1198,220 @@ const paginationSlice = createPaginationSlice({
 const { prevPage, nextPage, goToPage, toggleLoading } = paginationSlice.actions
 ```
 
+A creator could return a mix of reducer definitions for itself and other creators to handle:
+
+```ts no-transpile
+const historyCreatorType = Symbol()
+
+interface PatchesState {
+  undo: Patch[]
+  redo: Patch[]
+}
+
+interface HistoryState<T> {
+  past: PatchesState[]
+  present: T
+  future: PatchesState[]
+}
+
+declare module '@reduxjs/toolkit' {
+  export interface SliceReducerCreators<
+    State = any,
+    CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
+    Name extends string = string
+  > {
+    [paginationCreatorType]: {
+      // make sure the creator is only called when state is compatible
+      create(this: ReducerCreators<State>): State extends HistoryState<unknown>
+        ? {
+            undo: CaseReducerDefinition<State, PayloadAction>
+            redo: CaseReducerDefinition<State, PayloadAction>
+            reset: ReducerDefinition<typeof paginationCreatorType> & {
+              type: 'reset'
+            }
+          }
+        : never
+      actions: {
+        [ReducerName in ReducerNamesOfType<
+          CaseReducers,
+          typeof historyMethodsCreatorType
+        >]: CaseReducers[ReducerName] extends { type: 'reset' }
+          ? PayloadActionCreator<void, SliceActionType<Name, ReducerName>>
+          : never
+      }
+      caseReducers: {
+        [ReducerName in ReducerNamesOfType<
+          CaseReducers,
+          typeof historyMethodsCreatorType
+        >]: CaseReducers[ReducerName] extends { type: 'reset' }
+          ? CaseReducer<State, PayloadAction>
+          : never
+      }
+    }
+  }
+}
+
+const historyCreator: ReducerCreator<typeof historyCreatorType> = {
+  type: historyCreatorType,
+  create() {
+    return {
+      undo: this.reducer((state: HistoryState<unknown>) => {
+        const historyEntry = state.past.pop()
+        if (historyEntry) {
+          applyPatches(state, historyEntry.undo)
+          state.future.unshift(historyEntry)
+        }
+      }),
+      redo: this.reducer((state: HistoryState<unknown>) => {
+        const historyEntry = state.future.shift()
+        if (historyEntry) {
+          applyPatches(state, historyEntry.redo)
+          state.past.push(historyEntry)
+        }
+      }),
+      reset: {
+        _reducerDefinitionType: historyCreatorType,
+        type: 'reset',
+      },
+    }
+  },
+  handle(details, definition, context) {
+    if (definition.type !== 'reset') {
+      throw new Error('Unrecognised definition type: ' + definition.type)
+    }
+    // use the normal reducer creator to create a case reducer and action creator
+    const resetReducer = () => context.getInitialState()
+    reducerCreator.handle(details, reducerCreator.create(resetReducer), context)
+  },
+}
+
+const createHistorySlice = buildCreateSlice({
+  creators: { historyMethods: historyCreator },
+})
+
+function getInitialHistoryState<T>(initialState: HistoryState<T>) {
+  return {
+    past: [],
+    present: initialState,
+    future: [],
+  }
+}
+
+const postSliceWithHistory = createHistorySlice({
+  name: 'post',
+  initialState: getInitialHistoryState({ title: '' }),
+  reducers: (create) => ({
+    ...create.historyMethods(),
+  }),
+})
+
+const { undo, redo, reset } = postSliceWithHistory.actions
+```
+
 #### Other
+
+A creator doesn't have to return any reducer definitions, it could be any sort of utility for defining reducers.
+
+Following on from the `HistoryState` example above, it would be useful to make some sort of `undoable` utility to wrap reducers in logic which automatically updates the history of the slice.
+
+Fortunately, this is possible with a creator:
+
+```ts no-transpile
+const undoableCreatorType = Symbol()
+
+interface UndoableMeta {
+  undoable?: boolean
+}
+
+declare module '@reduxjs/toolkit' {
+  export interface SliceReducerCreators<
+    State = any,
+    CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
+    Name extends string = string
+  > {
+    [undoableCreatorType]: {
+      create: State extends HistoryState<infer Data>
+        ? {
+            reducer<A extends Action & { meta?: UndoableMeta }>(
+              reducer: CaseReducer<Data, A>
+            ): CaseReducer<State, A>
+            withoutPayload(options?: UndoableMeta): {
+              payload: undefined
+              meta: UndoableMeta | undefined
+            }
+            withPayload<Payload>(
+              payload: Payload,
+              options?: UndoableMeta
+            ): { payload: Payload; meta: UndoableMeta | undefined }
+          }
+        : never
+      actions: {}
+      caseReducers: {}
+    }
+  }
+}
+
+const undoableCreator: ReducerCreator<typeof undoableCreatorType> = {
+  type: undoableCreatorType,
+  create: {
+    reducer(reducer) {
+      return function wrappedReducer(state: HistoryState<unknown>, action) {
+        const [nextState, redoPatch, undoPatch] = produceWithPatches(
+          state,
+          (draft) => {
+            const result = reducer(draft.present, action)
+            if (typeof result !== 'undefined') {
+              draft.present = result
+            }
+          }
+        )
+        let finalState = nextState
+        const undoable = action.meta?.undoable ?? true
+        if (undoable) {
+          finalState = createNextState(finalState, (draft) => {
+            draft.past.push({
+              undo: undoPatch,
+              redo: redoPatch,
+            })
+            draft.future = []
+          })
+        }
+        return finalState
+      }
+    },
+    withoutPayload(options) {
+      return { payload: undefined, meta: options }
+    },
+    withPayload(payload, options) {
+      return { payload, meta: options }
+    },
+  },
+}
+
+const createHistorySlice = buildCreateSlice({
+  creators: { historyMethods: historyCreator, undoable: undoableCreator },
+})
+
+const postSliceWithHistory = createHistorySlice({
+  name: 'post',
+  initialState: getInitialHistoryState({ title: '', pinned: false }),
+  reducers: (create) => ({
+    ...create.historyMethods(),
+    updateTitle: create.preparedReducer(
+      create.undoable.withPayload<string>(),
+      create.undoable.reducer((state, action) => {
+        state.title = action.payload
+      })
+    ),
+    togglePinned: create.preparedReducer(
+      create.undoable.withoutPayload(),
+      create.undoable.reducer((state, action) => {
+        state.pinned = !state.pinned
+      })
+    ),
+  }),
+})
+
+const { undo, redo, reset, updateTitle, togglePinned } =
+  postSliceWithHistory.actions
+```
diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index ddfd20ac6d..3ede12e79d 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -1203,9 +1203,7 @@ describe('createSlice', () => {
             }
             reducerCreator.handle(
               details,
-              Object.assign(() => context.getInitialState(), {
-                _reducerDefinitionType: ReducerType.reducer as const,
-              }),
+              reducerCreator.create(() => context.getInitialState()),
               context
             )
           },

From 1256bcbe39289ab383245b70ef3bc1cbbbc8112d Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Tue, 2 Jan 2024 22:53:14 +0000
Subject: [PATCH 049/178] create must be a function

---
 docs/api/createSlice.mdx                      | 45 +++++++----
 .../toolkit/src/tests/createSlice.test.ts     | 79 +++++++++++--------
 2 files changed, 73 insertions(+), 51 deletions(-)

diff --git a/docs/api/createSlice.mdx b/docs/api/createSlice.mdx
index 0316422897..55047ee922 100644
--- a/docs/api/createSlice.mdx
+++ b/docs/api/createSlice.mdx
@@ -732,9 +732,9 @@ const reducerCreator: ReducerCreator<typeof reducerCreatorType> = {
 
 #### `create`
 
-The `create` property is the value that will be attached to the `create` object, before it's passed to the `reducers` callback.
+The `create` method is the function that will be attached to the `create` object, before it's passed to the `reducers` callback.
 
-Typically, it'll be a function (meaning the `this` value will be the final `create` object when called), but it could be anything - and the function could have additional methods attached.
+Because it's a function, the `this` value will be the final `create` object when called (assuming a `create.creator()` call). It also could have additional methods attached.
 
 The actual name the creator will be nested under is taken from the `buildCreateSlice` call. For example, `buildCreateSlice({ creators: { asyncThunk: asyncThunkCreator } })` results in the creator being available as `create.asyncThunk`.
 
@@ -837,7 +837,7 @@ All three keys must be provided, even if the creator only returns definitions fo
 
 #### `create`
 
-The type of the `create` property of the creator definition. Typically a function signature, but this isn't required.
+The signature of the `create` method of the creator definition.
 
 :::tip Using `this` to access other creators
 
@@ -1332,7 +1332,7 @@ declare module '@reduxjs/toolkit' {
     [undoableCreatorType]: {
       create: State extends HistoryState<infer Data>
         ? {
-            reducer<A extends Action & { meta?: UndoableMeta }>(
+            <A extends Action & { meta?: UndoableMeta }>(
               reducer: CaseReducer<Data, A>
             ): CaseReducer<State, A>
             withoutPayload(options?: UndoableMeta): {
@@ -1353,9 +1353,11 @@ declare module '@reduxjs/toolkit' {
 
 const undoableCreator: ReducerCreator<typeof undoableCreatorType> = {
   type: undoableCreatorType,
-  create: {
-    reducer(reducer) {
-      return function wrappedReducer(state: HistoryState<unknown>, action) {
+  create: Object.assign(
+    function makeUndoable<A extends Action & { meta?: UndoableOptions }>(
+      reducer: CaseReducer<any, A>
+    ): CaseReducer<HistoryState<any>, A> {
+      return (state, action) => {
         const [nextState, redoPatch, undoPatch] = produceWithPatches(
           state,
           (draft) => {
@@ -1379,13 +1381,24 @@ const undoableCreator: ReducerCreator<typeof undoableCreatorType> = {
         return finalState
       }
     },
-    withoutPayload(options) {
-      return { payload: undefined, meta: options }
-    },
-    withPayload(payload, options) {
-      return { payload, meta: options }
-    },
-  },
+    {
+      withoutPayload() {
+        return (options?: UndoableOptions) => ({
+          payload: undefined,
+          meta: options,
+        })
+      },
+      withPayload<P>() {
+        return (
+          ...[payload, options]: IfMaybeUndefined<
+            P,
+            [payload?: P, options?: UndoableOptions],
+            [payload: P, options?: UndoableOptions]
+          >
+        ) => ({ payload: payload as P, meta: options })
+      },
+    }
+  ),
 }
 
 const createHistorySlice = buildCreateSlice({
@@ -1399,13 +1412,13 @@ const postSliceWithHistory = createHistorySlice({
     ...create.historyMethods(),
     updateTitle: create.preparedReducer(
       create.undoable.withPayload<string>(),
-      create.undoable.reducer((state, action) => {
+      create.undoable((state, action) => {
         state.title = action.payload
       })
     ),
     togglePinned: create.preparedReducer(
       create.undoable.withoutPayload(),
-      create.undoable.reducer((state, action) => {
+      create.undoable((state, action) => {
         state.pinned = !state.pinned
       })
     ),
diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index 3ede12e79d..8c8aa3b1de 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -1209,45 +1209,54 @@ describe('createSlice', () => {
           },
         }
 
-        const makeUndoable = function makeUndoable(reducer) {
-          return function wrappedReducer(state: HistoryState<any>, action) {
-            const [nextState, redoPatch, undoPatch] = produceWithPatches(
-              state,
-              (draft) => {
-                const result = reducer(draft.present, action)
-                if (typeof result !== 'undefined') {
-                  draft.present = result
+        const undoableCreator: ReducerCreator<typeof undoableCreatorType> = {
+          type: undoableCreatorType,
+          create: Object.assign(
+            function makeUndoable<
+              A extends Action & { meta?: UndoableOptions }
+            >(reducer: CaseReducer<any, A>): CaseReducer<HistoryState<any>, A> {
+              return (state, action) => {
+                const [nextState, redoPatch, undoPatch] = produceWithPatches(
+                  state,
+                  (draft) => {
+                    const result = reducer(draft.present, action)
+                    if (typeof result !== 'undefined') {
+                      draft.present = result
+                    }
+                  }
+                )
+                let finalState = nextState
+                const undoable = action.meta?.undoable ?? true
+                if (undoable) {
+                  finalState = createNextState(finalState, (draft) => {
+                    draft.past.push({
+                      undo: undoPatch,
+                      redo: redoPatch,
+                    })
+                    draft.future = []
+                  })
                 }
+                return finalState
               }
-            )
-            let finalState = nextState
-            const undoable = action.meta?.undoable ?? true
-            if (undoable) {
-              finalState = createNextState(finalState, (draft) => {
-                draft.past.push({
-                  undo: undoPatch,
-                  redo: redoPatch,
+            },
+            {
+              withoutPayload() {
+                return (options?: UndoableOptions) => ({
+                  payload: undefined,
+                  meta: options,
                 })
-                draft.future = []
-              })
+              },
+              withPayload<P>() {
+                return (
+                  ...[payload, options]: IfMaybeUndefined<
+                    P,
+                    [payload?: P, options?: UndoableOptions],
+                    [payload: P, options?: UndoableOptions]
+                  >
+                ) => ({ payload: payload as P, meta: options })
+              },
             }
-            return finalState
-          }
-        } as ReducerCreator<typeof undoableCreatorType>['create']
-
-        makeUndoable.withoutPayload = () => (options) => ({
-          payload: undefined,
-          meta: options,
-        })
-        makeUndoable.withPayload = (() =>
-          (payload: unknown, options?: UndoableOptions) => ({
-            payload,
-            meta: options,
-          })) as any
-
-        const undoableCreator: ReducerCreator<typeof undoableCreatorType> = {
-          type: undoableCreatorType,
-          create: makeUndoable,
+          ),
         }
 
         const createHistorySlice = buildCreateSlice({

From cecda64fedf01c1181dcabcc721ab0823ada3366 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Tue, 2 Jan 2024 23:31:44 +0000
Subject: [PATCH 050/178] corrections

---
 docs/api/createSlice.mdx | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/docs/api/createSlice.mdx b/docs/api/createSlice.mdx
index 55047ee922..dadfe2472e 100644
--- a/docs/api/createSlice.mdx
+++ b/docs/api/createSlice.mdx
@@ -704,7 +704,7 @@ Typically a creator will return a [single reducer definition](#single-definition
 
 ### Creator definitions
 
-A creator definition is an object with a `type` property, a `create` property, and an optional `handle` method.
+A creator definition is an object with a `type` property, a `create` method, and an optional `handle` method.
 
 The `type` property should be the same constant used for reducer definitions to be handled by this creator. To avoid collision, we recommend using Symbols for this. It's also used for defining/retrieving types - see [Typescript](#typescript).
 
@@ -1289,7 +1289,7 @@ const createHistorySlice = buildCreateSlice({
   creators: { historyMethods: historyCreator },
 })
 
-function getInitialHistoryState<T>(initialState: HistoryState<T>) {
+function getInitialHistoryState<T>(initialState: T): HistoryState<T> {
   return {
     past: [],
     present: initialState,

From 9e40fd2cfab7b6ee4ed27c57839a390a66eec4a2 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.durrant@marketdojo.com>
Date: Wed, 3 Jan 2024 10:32:19 +0000
Subject: [PATCH 051/178] Add ReducerCreatorEntry type to streamline creator
 registry

---
 packages/toolkit/src/createSlice.ts           | 200 +++++++++-------
 packages/toolkit/src/index.ts                 |   1 +
 .../toolkit/src/tests/createSlice.test.ts     | 221 +++++++++---------
 3 files changed, 227 insertions(+), 195 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 41363849d3..5382de5e9d 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -16,7 +16,12 @@ import type {
 import { createReducer, makeGetInitialState } from './createReducer'
 import type { ActionReducerMapBuilder, TypedActionCreator } from './mapBuilders'
 import { executeReducerBuilderCallback } from './mapBuilders'
-import type { Id, TypeGuard, UnionToIntersection } from './tsHelpers'
+import type {
+  Id,
+  IfMaybeUndefined,
+  TypeGuard,
+  UnionToIntersection,
+} from './tsHelpers'
 import type { InjectConfig } from './combineSlices'
 import type {
   AsyncThunk,
@@ -42,104 +47,129 @@ export interface ReducerDefinition<
   _reducerDefinitionType: T
 }
 
+export type ReducerCreatorEntry<
+  Create extends (...args: any[]) => any,
+  Exposes extends {
+    actions?: Record<string, unknown>
+    caseReducers?: Record<string, unknown>
+  } = { actions: {}; caseReducers: {} }
+> = {
+  create: Create
+  actions: IfMaybeUndefined<Exposes['actions'], {}, Exposes['actions']>
+  caseReducers: IfMaybeUndefined<
+    Exposes['caseReducers'],
+    {},
+    Exposes['caseReducers']
+  >
+}
+
 export interface SliceReducerCreators<
   State = any,
   CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
   Name extends string = string
 > {
-  [ReducerType.reducer]: {
-    create(
-      caseReducer: CaseReducer<State, PayloadAction>
-    ): CaseReducerDefinition<State, PayloadAction>
-    create<Payload = any>(
-      caseReducer: CaseReducer<State, PayloadAction<Payload>>
-    ): CaseReducerDefinition<State, PayloadAction<Payload>>
-    actions: {
-      [ReducerName in keyof CaseReducers as CaseReducers[ReducerName] extends CaseReducer<
-        State,
-        any
-      >
-        ? ReducerName
-        : never]: ActionCreatorForCaseReducer<
-        CaseReducers[ReducerName],
-        SliceActionType<Name, ReducerName>
-      >
-    }
-    caseReducers: {
-      [ReducerName in keyof CaseReducers as CaseReducers[ReducerName] extends CaseReducer<
+  [ReducerType.reducer]: ReducerCreatorEntry<
+    {
+      (caseReducer: CaseReducer<State, PayloadAction>): CaseReducerDefinition<
         State,
-        any
+        PayloadAction
       >
-        ? ReducerName
-        : never]: CaseReducers[ReducerName]
+      <Payload = any>(
+        caseReducer: CaseReducer<State, PayloadAction<Payload>>
+      ): CaseReducerDefinition<State, PayloadAction<Payload>>
+    },
+    {
+      actions: {
+        [ReducerName in keyof CaseReducers as CaseReducers[ReducerName] extends CaseReducer<
+          State,
+          any
+        >
+          ? ReducerName
+          : never]: ActionCreatorForCaseReducer<
+          CaseReducers[ReducerName],
+          SliceActionType<Name, ReducerName>
+        >
+      }
+      caseReducers: {
+        [ReducerName in keyof CaseReducers as CaseReducers[ReducerName] extends CaseReducer<
+          State,
+          any
+        >
+          ? ReducerName
+          : never]: CaseReducers[ReducerName]
+      }
     }
-  }
-  [ReducerType.reducerWithPrepare]: {
-    create<Prepare extends PrepareAction<any>>(
+  >
+  [ReducerType.reducerWithPrepare]: ReducerCreatorEntry<
+    <Prepare extends PrepareAction<any>>(
       prepare: Prepare,
       reducer: CaseReducer<
         State,
         ReturnType<_ActionCreatorWithPreparedPayload<Prepare>>
       >
-    ): PreparedCaseReducerDefinition<State, Prepare>
-    actions: {
-      [ReducerName in keyof CaseReducers as CaseReducers[ReducerName] extends CaseReducerWithPrepare<
-        State,
-        any
-      >
-        ? ReducerName
-        : never]: CaseReducers[ReducerName] extends { prepare: any }
-        ? ActionCreatorForCaseReducerWithPrepare<
-            CaseReducers[ReducerName],
-            SliceActionType<Name, ReducerName>
-          >
-        : never
-    }
-    caseReducers: {
-      [ReducerName in keyof CaseReducers as CaseReducers[ReducerName] extends CaseReducerWithPrepare<
-        State,
-        any
-      >
-        ? ReducerName
-        : never]: CaseReducers[ReducerName] extends { reducer: infer Reducer }
-        ? Reducer
-        : never
-    }
-  }
-  [ReducerType.asyncThunk]: {
-    create: AsyncThunkCreator<State>
-    actions: {
-      [ReducerName in ReducerNamesOfType<
-        CaseReducers,
-        ReducerType.asyncThunk
-      >]: CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition<
-        State,
-        infer ThunkArg,
-        infer Returned,
-        infer ThunkApiConfig
-      >
-        ? AsyncThunk<Returned, ThunkArg, ThunkApiConfig>
-        : never
+    ) => PreparedCaseReducerDefinition<State, Prepare>,
+    {
+      actions: {
+        [ReducerName in keyof CaseReducers as CaseReducers[ReducerName] extends CaseReducerWithPrepare<
+          State,
+          any
+        >
+          ? ReducerName
+          : never]: CaseReducers[ReducerName] extends { prepare: any }
+          ? ActionCreatorForCaseReducerWithPrepare<
+              CaseReducers[ReducerName],
+              SliceActionType<Name, ReducerName>
+            >
+          : never
+      }
+      caseReducers: {
+        [ReducerName in keyof CaseReducers as CaseReducers[ReducerName] extends CaseReducerWithPrepare<
+          State,
+          any
+        >
+          ? ReducerName
+          : never]: CaseReducers[ReducerName] extends { reducer: infer Reducer }
+          ? Reducer
+          : never
+      }
     }
-    caseReducers: {
-      [ReducerName in ReducerNamesOfType<
-        CaseReducers,
-        ReducerType.asyncThunk
-      >]: CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition<
-        State,
-        any,
-        any,
-        any
-      >
-        ? Id<
-            Pick<
-              Required<CaseReducers[ReducerName]>,
-              'fulfilled' | 'rejected' | 'pending' | 'settled'
+  >
+  [ReducerType.asyncThunk]: ReducerCreatorEntry<
+    AsyncThunkCreator<State>,
+    {
+      actions: {
+        [ReducerName in ReducerNamesOfType<
+          CaseReducers,
+          ReducerType.asyncThunk
+        >]: CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition<
+          State,
+          infer ThunkArg,
+          infer Returned,
+          infer ThunkApiConfig
+        >
+          ? AsyncThunk<Returned, ThunkArg, ThunkApiConfig>
+          : never
+      }
+      caseReducers: {
+        [ReducerName in ReducerNamesOfType<
+          CaseReducers,
+          ReducerType.asyncThunk
+        >]: CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition<
+          State,
+          any,
+          any,
+          any
+        >
+          ? Id<
+              Pick<
+                Required<CaseReducers[ReducerName]>,
+                'fulfilled' | 'rejected' | 'pending' | 'settled'
+              >
             >
-          >
-        : never
+          : never
+      }
     }
-  }
+  >
 }
 
 export type ReducerCreators<
@@ -1155,9 +1185,9 @@ export function buildCreateSlice<
 }
 
 function wrapSelector<State, NewState, S extends Selector<State>>(
-  slice: Slice<State, any>,
+  slice: { getInitialState(): State; reducerPath: string },
   selector: S,
-  selectState: Selector<NewState, State>,
+  selectState: (this: typeof slice, rootState: NewState) => State,
   injected?: boolean
 ) {
   function wrapper(rootState: NewState, ...args: any[]) {
diff --git a/packages/toolkit/src/index.ts b/packages/toolkit/src/index.ts
index 85f3e07315..12c14a8d9e 100644
--- a/packages/toolkit/src/index.ts
+++ b/packages/toolkit/src/index.ts
@@ -84,6 +84,7 @@ export type {
   SliceReducerCreators,
   ReducerDefinition,
   ReducerNamesOfType,
+  ReducerCreatorEntry,
   ReducerCreator,
   SliceActionType,
   CaseReducerDefinition,
diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index 8c8aa3b1de..e07e6fa655 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -10,6 +10,7 @@ import type {
   PayloadActionCreator,
   PreparedCaseReducerDefinition,
   ReducerCreator,
+  ReducerCreatorEntry,
   ReducerCreators,
   ReducerDefinition,
   ReducerNamesOfType,
@@ -1397,144 +1398,146 @@ declare module '@reduxjs/toolkit' {
     CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
     Name extends string = string
   > {
-    [loaderCreatorType]: {
-      create(
+    [loaderCreatorType]: ReducerCreatorEntry<
+      (
         reducers: Pick<LoaderReducerDefinition<State>, 'ended' | 'started'>
-      ): LoaderReducerDefinition<State>
-      actions: {
-        [ReducerName in ReducerNamesOfType<
-          CaseReducers,
-          typeof loaderCreatorType
-        >]: (() => ThunkAction<
-          { loaderId: string; end: () => void },
-          unknown,
-          unknown,
-          Action
-        >) & {
-          started: PayloadActionCreator<
-            string,
-            `${SliceActionType<Name, ReducerName>}/started`
-          >
-          ended: PayloadActionCreator<
-            string,
-            `${SliceActionType<Name, ReducerName>}/ended`
+      ) => LoaderReducerDefinition<State>,
+      {
+        actions: {
+          [ReducerName in ReducerNamesOfType<
+            CaseReducers,
+            typeof loaderCreatorType
+          >]: (() => ThunkAction<
+            { loaderId: string; end: () => void },
+            unknown,
+            unknown,
+            Action
+          >) & {
+            started: PayloadActionCreator<
+              string,
+              `${SliceActionType<Name, ReducerName>}/started`
+            >
+            ended: PayloadActionCreator<
+              string,
+              `${SliceActionType<Name, ReducerName>}/ended`
+            >
+          }
+        }
+        caseReducers: {
+          [ReducerName in ReducerNamesOfType<
+            CaseReducers,
+            typeof loaderCreatorType
+          >]: Required<
+            Pick<LoaderReducerDefinition<State>, 'ended' | 'started'>
           >
         }
       }
-      caseReducers: {
-        [ReducerName in ReducerNamesOfType<
-          CaseReducers,
-          typeof loaderCreatorType
-        >]: Required<Pick<LoaderReducerDefinition<State>, 'ended' | 'started'>>
-      }
-    }
-    [conditionCreatorType]: {
-      create<Args extends any[]>(
+    >
+    [conditionCreatorType]: ReducerCreatorEntry<
+      <Args extends any[]>(
         makePredicate: (...args: Args) => AnyListenerPredicate<unknown>
-      ): ReducerDefinition<typeof conditionCreatorType> & {
+      ) => ReducerDefinition<typeof conditionCreatorType> & {
         makePredicate: (...args: Args) => AnyListenerPredicate<unknown>
-      }
-      actions: {
-        [ReducerName in ReducerNamesOfType<
-          CaseReducers,
-          typeof conditionCreatorType
-        >]: CaseReducers[ReducerName] extends {
-          makePredicate: (...args: infer Args) => AnyListenerPredicate<unknown>
+      },
+      {
+        actions: {
+          [ReducerName in ReducerNamesOfType<
+            CaseReducers,
+            typeof conditionCreatorType
+          >]: CaseReducers[ReducerName] extends {
+            makePredicate: (
+              ...args: infer Args
+            ) => AnyListenerPredicate<unknown>
+          }
+            ? (
+                timeout?: number,
+                ...args: Args
+              ) => ThunkAction<
+                Promise<boolean> & { unsubscribe?: UnsubscribeListener },
+                unknown,
+                unknown,
+                UnknownAction
+              >
+            : never
         }
-          ? (
-              timeout?: number,
-              ...args: Args
-            ) => ThunkAction<
-              Promise<boolean> & { unsubscribe?: UnsubscribeListener },
-              unknown,
-              unknown,
-              UnknownAction
-            >
-          : never
       }
-      caseReducers: {}
-    }
-    [fetchCreatorType]: {
-      create(this: ReducerCreators<State>): State extends FetchState<infer Data>
-        ? {
+    >
+    [fetchCreatorType]: ReducerCreatorEntry<
+      State extends FetchState<infer Data>
+        ? (this: ReducerCreators<State>) => {
             start: CaseReducerDefinition<State, PayloadAction>
             success: CaseReducerDefinition<State, PayloadAction<Data>>
           }
         : never
-      actions: {}
-      caseReducers: {}
-    }
-    [paginationCreatorType]: {
-      create(this: ReducerCreators<State>): State extends PaginationState
-        ? {
+    >
+    [paginationCreatorType]: ReducerCreatorEntry<
+      State extends PaginationState
+        ? (this: ReducerCreators<State>) => {
             nextPage: CaseReducerDefinition<State, PayloadAction>
             previousPage: CaseReducerDefinition<State, PayloadAction>
             goToPage: CaseReducerDefinition<State, PayloadAction<number>>
           }
         : never
-      actions: {}
-      caseReducers: {}
-    }
-    [historyMethodsCreatorType]: {
-      create(this: ReducerCreators<State>): State extends HistoryState<unknown>
-        ? {
+    >
+    [historyMethodsCreatorType]: ReducerCreatorEntry<
+      State extends HistoryState<unknown>
+        ? (this: ReducerCreators<State>) => {
             undo: CaseReducerDefinition<State, PayloadAction>
             redo: CaseReducerDefinition<State, PayloadAction>
             reset: ReducerDefinition<typeof historyMethodsCreatorType> & {
               type: 'reset'
             }
           }
-        : never
-      actions: {
-        [ReducerName in ReducerNamesOfType<
-          CaseReducers,
-          typeof historyMethodsCreatorType
-        >]: CaseReducers[ReducerName] extends { type: 'reset' }
-          ? PayloadActionCreator<void, SliceActionType<Name, ReducerName>>
-          : never
-      }
-      caseReducers: {
-        [ReducerName in ReducerNamesOfType<
-          CaseReducers,
-          typeof historyMethodsCreatorType
-        >]: CaseReducers[ReducerName] extends { type: 'reset' }
-          ? CaseReducer<State, PayloadAction>
-          : never
-      }
-    }
-    [undoableCreatorType]: {
-      create: {
-        <A extends Action & { meta?: UndoableOptions }>(
-          this: ReducerCreators<State>,
-          reducer: CaseReducer<
-            State extends HistoryState<infer Data> ? Data : never,
-            NoInfer<A>
-          >
-        ): CaseReducer<State, A>
-        withoutPayload(): (options?: UndoableOptions) => {
-          payload: undefined
-          meta: UndoableOptions | undefined
+        : never,
+      {
+        actions: {
+          [ReducerName in ReducerNamesOfType<
+            CaseReducers,
+            typeof historyMethodsCreatorType
+          >]: CaseReducers[ReducerName] extends { type: 'reset' }
+            ? PayloadActionCreator<void, SliceActionType<Name, ReducerName>>
+            : never
+        }
+        caseReducers: {
+          [ReducerName in ReducerNamesOfType<
+            CaseReducers,
+            typeof historyMethodsCreatorType
+          >]: CaseReducers[ReducerName] extends { type: 'reset' }
+            ? CaseReducer<State, PayloadAction>
+            : never
         }
-        withPayload<P>(): (
-          ...args: IfMaybeUndefined<
-            P,
-            [payload?: P, options?: UndoableOptions],
-            [payload: P, options?: UndoableOptions]
-          >
-        ) => { payload: P; meta: UndoableOptions | undefined }
       }
-      actions: {}
-      caseReducers: {}
-    }
-    [batchedCreatorType]: {
-      create(
+    >
+    [undoableCreatorType]: ReducerCreatorEntry<
+      State extends HistoryState<infer Data>
+        ? {
+            <A extends Action & { meta?: UndoableOptions }>(
+              this: ReducerCreators<State>,
+              reducer: CaseReducer<Data, NoInfer<A>>
+            ): CaseReducer<State, A>
+            withoutPayload(): (options?: UndoableOptions) => {
+              payload: undefined
+              meta: UndoableOptions | undefined
+            }
+            withPayload<P>(): (
+              ...args: IfMaybeUndefined<
+                P,
+                [payload?: P, options?: UndoableOptions],
+                [payload: P, options?: UndoableOptions]
+              >
+            ) => { payload: P; meta: UndoableOptions | undefined }
+          }
+        : never
+    >
+    [batchedCreatorType]: ReducerCreatorEntry<{
+      (
         this: ReducerCreators<State>,
         reducer: CaseReducer<State, PayloadAction>
       ): PreparedCaseReducerDefinition<
         State,
         () => { payload: undefined; meta: unknown }
       >
-      create<Payload>(
+      <Payload>(
         this: ReducerCreators<State>,
         reducer: CaseReducer<State, PayloadAction<Payload>>
       ): PreparedCaseReducerDefinition<
@@ -1545,8 +1548,6 @@ declare module '@reduxjs/toolkit' {
           (payload: Payload) => { payload: Payload; meta: unknown }
         >
       >
-      actions: {}
-      caseReducers: {}
-    }
+    }>
   }
 }

From 30f3b2efbfe0d83f181d3412a385fb243e867734 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.durrant@marketdojo.com>
Date: Wed, 3 Jan 2024 10:34:38 +0000
Subject: [PATCH 052/178] simplify default

---
 packages/toolkit/src/createSlice.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 5382de5e9d..b7b44bec16 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -52,7 +52,7 @@ export type ReducerCreatorEntry<
   Exposes extends {
     actions?: Record<string, unknown>
     caseReducers?: Record<string, unknown>
-  } = { actions: {}; caseReducers: {} }
+  } = {}
 > = {
   create: Create
   actions: IfMaybeUndefined<Exposes['actions'], {}, Exposes['actions']>

From 87877332ced7a6f0f4323184d3ae7c66cec91087 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.durrant@marketdojo.com>
Date: Wed, 3 Jan 2024 11:09:44 +0000
Subject: [PATCH 053/178] search attached methods for possible definitions too

---
 packages/toolkit/src/createSlice.ts | 18 +++++++++++++-----
 1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index b7b44bec16..ae4cc94bf0 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -285,11 +285,19 @@ type RecursiveExtractDefinition<
         }[keyof Definitions]
       : never)
 
-type ReducerDefinitionsForType<Type extends RegisteredReducerType> = {
-  [CreatorType in keyof SliceReducerCreators]: RecursiveExtractDefinition<
-    ReturnType<SliceReducerCreators[CreatorType]['create']>,
-    Type
-  >
+export type ReducerDefinitionsForType<Type extends RegisteredReducerType> = {
+  [CreatorType in keyof SliceReducerCreators]:
+    | RecursiveExtractDefinition<
+        ReturnType<SliceReducerCreators[CreatorType]['create']>,
+        Type
+      >
+    | {
+        [K in keyof SliceReducerCreators[CreatorType]['create']]: SliceReducerCreators[CreatorType]['create'][K] extends (
+          ...args: any[]
+        ) => infer Definitions
+          ? RecursiveExtractDefinition<Definitions, Type>
+          : never
+      }[keyof SliceReducerCreators[CreatorType]['create']]
 }[keyof SliceReducerCreators]
 
 export type ReducerCreator<Type extends RegisteredReducerType> = {

From cd9f0f91e00282d9574df1f80fd5a2cb388c3279 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.durrant@marketdojo.com>
Date: Wed, 3 Jan 2024 11:12:51 +0000
Subject: [PATCH 054/178] test attached method returning definition

---
 packages/toolkit/src/createSlice.ts           |  2 +-
 .../toolkit/src/tests/createSlice.test.ts     | 20 +++++++++++++++----
 2 files changed, 17 insertions(+), 5 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index ae4cc94bf0..9d91c19415 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -285,7 +285,7 @@ type RecursiveExtractDefinition<
         }[keyof Definitions]
       : never)
 
-export type ReducerDefinitionsForType<Type extends RegisteredReducerType> = {
+type ReducerDefinitionsForType<Type extends RegisteredReducerType> = {
   [CreatorType in keyof SliceReducerCreators]:
     | RecursiveExtractDefinition<
         ReturnType<SliceReducerCreators[CreatorType]['create']>,
diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index e07e6fa655..9cfe757d32 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -44,7 +44,6 @@ import {
   getLog,
 } from 'console-testing-library/pure'
 import type { IfMaybeUndefined, NoInfer } from '../tsHelpers'
-
 enablePatches()
 
 type CreateSlice = typeof createSlice
@@ -1321,10 +1320,22 @@ describe('createSlice', () => {
       test('batchable', () => {
         const batchedCreator: ReducerCreator<typeof batchedCreatorType> = {
           type: batchedCreatorType,
-          create(this: ReducerCreators<any>, reducer: CaseReducer<any, any>) {
-            return this.preparedReducer(prepareAutoBatched(), reducer) as any
-          },
+          create: Object.assign(
+            function (
+              this: ReducerCreators<any>,
+              reducer: CaseReducer<any, any>
+            ) {
+              return this.preparedReducer(prepareAutoBatched(), reducer) as any
+            },
+            {
+              test() {
+                return { _reducerDefinitionType: batchedCreatorType } as const
+              },
+            }
+          ),
+          handle() {},
         }
+
         const createBatchSlice = buildCreateSlice({
           creators: { batchedReducer: batchedCreator },
         })
@@ -1548,6 +1559,7 @@ declare module '@reduxjs/toolkit' {
           (payload: Payload) => { payload: Payload; meta: unknown }
         >
       >
+      test(): ReducerDefinition<typeof batchedCreatorType>
     }>
   }
 }

From 33015e5b63890941ba7c1cdf6136eadf7027c831 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.durrant@marketdojo.com>
Date: Wed, 3 Jan 2024 11:37:22 +0000
Subject: [PATCH 055/178] use ReducerCreatorEntry type in docs

---
 docs/api/createSlice.mdx | 186 +++++++++++++++++++--------------------
 1 file changed, 92 insertions(+), 94 deletions(-)

diff --git a/docs/api/createSlice.mdx b/docs/api/createSlice.mdx
index dadfe2472e..13720a6fd6 100644
--- a/docs/api/createSlice.mdx
+++ b/docs/api/createSlice.mdx
@@ -809,7 +809,7 @@ context
 
 The Typescript system for custom slice creators uses a "creator registry" system similar to the module system for [RTK Query](/rtk-query/usage/customizing-create-api#creating-your-own-module).
 
-Creators are registered by using module augmentation to add a new key (their unique `type`) to the `SliceReducerCreators` interface. The interface receives three type parameters (`State`, `CaseReducers` and `Name`), and each entry should have three keys (`create`, `actions`, and `caseReducers`).
+Creators are registered by using module augmentation to add a new key (their unique `type`) to the `SliceReducerCreators` interface. The interface receives three type parameters (`State`, `CaseReducers` and `Name`), and each entry should have use the `ReducerCreatorEntry` type utility.
 
 ```ts no-transpile
 const reducerCreatorType = Symbol()
@@ -820,22 +820,16 @@ declare module '@reduxjs/toolkit' {
     CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
     Name extends string = string
   > {
-    [reducerCreatorType]: {
-      create(): ReducerDefinition<typeof reducerCreatorType>
-      actions: {}
-      caseReducers: {}
-    }
+    [reducerCreatorType]: ReducerCreatorEntry<
+      () => ReducerDefinition<typeof reducerCreatorType>
+    >
   }
 }
 ```
 
-:::danger
-
-All three keys must be provided, even if the creator only returns definitions for other creators. If the creator doesn't expose any actions/case reducers itself, leave those keys as `{}`.
+The `ReducerCreatorEntry<Create, Exposes>` utility has two type parameters:
 
-:::
-
-#### `create`
+#### `Create`
 
 The signature of the `create` method of the creator definition.
 
@@ -854,18 +848,16 @@ declare module '@reduxjs/toolkit' {
     CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
     Name extends string = string
   > {
-    [batchedCreatorType]: {
-      create<Payload>(
+    [batchedCreatorType]: ReducerCreatorEntry<
+      <Payload>(
         // highlight-next-line
         this: ReducerCreators<State, {}>,
         reducer: CaseReducer<State, PayloadAction<Payload>>
-      ): PreparedCaseReducerDefinition<
+      ) => PreparedCaseReducerDefinition<
         State,
         (payload: Payload) => { payload: Payload; meta: unknown }
       >
-      actions: {}
-      caseReducers: {}
-    }
+    >
   }
 }
 
@@ -881,7 +873,13 @@ The second argument to the `ReducerCreators` type is a map from creator names to
 
 :::
 
-#### `actions`
+#### `Exposes`
+
+The second type parameter for `ReducerCreatorEntry` is optional, but should be used if the creator will handle some reducer definitions itself. It indicates what actions and case reducers will be attached to the slice, and is used to determine the final types of `slice.actions` and `slice.caseReducers`.
+
+It should be an object with some of the following properties:
+
+##### `actions`
 
 The actions property will typically be a [mapped type](https://www.typescriptlang.org/docs/handbook/2/mapped-types.html) over the `CaseReducers` type parameter, returning what the creator's `handle` would expose when given that definition.
 
@@ -898,33 +896,32 @@ declare module '@reduxjs/toolkit' {
     CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
     Name extends string = string
   > {
-    [asyncThunkCreatorType]: {
-      create<ThunkArg, Returned>(
+    [asyncThunkCreatorType]: ReducerCreatorEntry<
+      <ThunkArg, Returned>(
         payloadCreator: AsyncThunkPayloadCreator<ThunkArg, Returned>
-      ): AsyncThunkReducerDefinition<State, ThunkArg, Returned>
-      // highlight-start
-      actions: {
-        [ReducerName in ReducerNamesOfType<
-          CaseReducers,
-          typeof asyncThunkCreatorType
-        >]: CaseReducers[ReducerName] extends AsyncThunkReducerDefinition<
-          State,
-          infer ThunkArg,
-          infer Returned
-        >
-          ? AsyncThunk<ThunkArg, Returned>
-          : never
+      ) => AsyncThunkReducerDefinition<State, ThunkArg, Returned>,
+      {
+        // highlight-start
+        actions: {
+          [ReducerName in ReducerNamesOfType<
+            CaseReducers,
+            typeof asyncThunkCreatorType
+          >]: CaseReducers[ReducerName] extends AsyncThunkReducerDefinition<
+            State,
+            infer ThunkArg,
+            infer Returned
+          >
+            ? AsyncThunk<ThunkArg, Returned>
+            : never
+        }
+        // highlight-end
       }
-      // highlight-end
-      caseReducers: {}
-    }
+    >
   }
 }
 ```
 
-The `actions` returned from all the creators when passed the `CaseReducers` definitions are all intersected to create the final type for `slice.actions`.
-
-#### `caseReducers`
+##### `caseReducers`
 
 Similar to `actions`, except for `slice.caseReducers`.
 
@@ -939,33 +936,35 @@ declare module '@reduxjs/toolkit' {
     CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
     Name extends string = string
   > {
-    [preparedReducerType]: {
-      create<Prepare extends PrepareAction<any>>(
+    [preparedReducerType]: ReducerCreatorEntry<
+      <Prepare extends PrepareAction<any>>(
         prepare: Prepare,
         caseReducer: CaseReducer<State, ActionForPrepare<Prepare>>
-      ): PreparedCaseReducerDefinition<State, Prepare>
-      actions: {
-        [ReducerName in ReducerNamesOfType<
-          CaseReducers,
-          typeof preparedReducerType
-        >]: CaseReducers[ReducerName] extends { prepare: any }
-          ? ActionCreatorForCaseReducerWithPrepare<
-              CaseReducers[ReducerName],
-              SliceActionType<Name, ReducerName>
-            >
-          : never
-      }
-      // highlight-start
-      caseReducers: {
-        [ReducerName in ReducerNamesOfType<
-          CaseReducers,
-          typeof preparedReducerType
-        >]: CaseReducers[ReducerName] extends { reducer: infer Reducer }
-          ? Reducer
-          : never
+      ) => PreparedCaseReducerDefinition<State, Prepare>,
+      {
+        actions: {
+          [ReducerName in ReducerNamesOfType<
+            CaseReducers,
+            typeof preparedReducerType
+          >]: CaseReducers[ReducerName] extends { prepare: any }
+            ? ActionCreatorForCaseReducerWithPrepare<
+                CaseReducers[ReducerName],
+                SliceActionType<Name, ReducerName>
+              >
+            : never
+        }
+        // highlight-start
+        caseReducers: {
+          [ReducerName in ReducerNamesOfType<
+            CaseReducers,
+            typeof preparedReducerType
+          >]: CaseReducers[ReducerName] extends { reducer: infer Reducer }
+            ? Reducer
+            : never
+        }
+        // highlight-end
       }
-      // highlight-end
-    }
+    >
   }
 }
 ```
@@ -1022,21 +1021,24 @@ declare module '@reduxjs/toolkit' {
     CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
     Name extends string = string
   > {
-    [toastCreatorType]: {
-      create(
+    [toastCreatorType]: ReducerCreatorEntry<
+      (
         config: ToastReducerConfig<State>
-      ): ToastReducerConfig<State> & ReducerDefinition<typeof toastCreatorType>
-      actions: {
-        [ReducerName in ReducerNamesOfType<
-          typeof toastCreatorType
-        >]: ToastThunkCreator<Name, ReducerName>
-      }
-      caseReducers: {
-        [ReducerName in ReducerNamesOfType<typeof toastCreatorType>]: Required<
-          ToastReducerConfig<State>
-        >
+      ) => ToastReducerConfig<State> &
+        ReducerDefinition<typeof toastCreatorType>,
+      {
+        actions: {
+          [ReducerName in ReducerNamesOfType<
+            typeof toastCreatorType
+          >]: ToastThunkCreator<Name, ReducerName>
+        }
+        caseReducers: {
+          [ReducerName in ReducerNamesOfType<
+            typeof toastCreatorType
+          >]: Required<ToastReducerConfig<State>>
+        }
       }
-    }
+    >
   }
 }
 
@@ -1148,18 +1150,16 @@ declare module '@reduxjs/toolkit' {
     CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
     Name extends string = string
   > {
-    [paginationCreatorType]: {
+    [paginationCreatorType]: ReducerCreatorEntry<
       // make sure the creator is only called when state is compatible
-      create(this: ReducerCreators<State>): State extends PaginationState
-        ? {
+      State extends PaginationState
+        ? (this: ReducerCreators<State>) => {
             prevPage: CaseReducerDefinition<State, PayloadAction>
             nextPage: CaseReducerDefinition<State, PayloadAction>
             goToPage: CaseReducerDefinition<State, PayloadAction<number>>
           }
         : never
-      actions: {}
-      caseReducers: {}
-    }
+    >
   }
 }
 
@@ -1220,17 +1220,17 @@ declare module '@reduxjs/toolkit' {
     CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
     Name extends string = string
   > {
-    [paginationCreatorType]: {
-      // make sure the creator is only called when state is compatible
-      create(this: ReducerCreators<State>): State extends HistoryState<unknown>
-        ? {
+    [paginationCreatorType]: ReducerCreatorEntry<
+      // make sure the creator is only called when state is compatibleState extends HistoryState<unknown>
+        ?
+      (this: ReducerCreators<State>) => {
             undo: CaseReducerDefinition<State, PayloadAction>
             redo: CaseReducerDefinition<State, PayloadAction>
             reset: ReducerDefinition<typeof paginationCreatorType> & {
               type: 'reset'
             }
           }
-        : never
+        : never, {
       actions: {
         [ReducerName in ReducerNamesOfType<
           CaseReducers,
@@ -1247,7 +1247,7 @@ declare module '@reduxjs/toolkit' {
           ? CaseReducer<State, PayloadAction>
           : never
       }
-    }
+    }>
   }
 }
 
@@ -1329,8 +1329,8 @@ declare module '@reduxjs/toolkit' {
     CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
     Name extends string = string
   > {
-    [undoableCreatorType]: {
-      create: State extends HistoryState<infer Data>
+    [undoableCreatorType]: ReducerCreatorEntry<
+      State extends HistoryState<infer Data>
         ? {
             <A extends Action & { meta?: UndoableMeta }>(
               reducer: CaseReducer<Data, A>
@@ -1345,9 +1345,7 @@ declare module '@reduxjs/toolkit' {
             ): { payload: Payload; meta: UndoableMeta | undefined }
           }
         : never
-      actions: {}
-      caseReducers: {}
-    }
+    >
   }
 }
 

From b299631ebf7a01ca7275edea5c0046e547d91cc5 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.durrant@marketdojo.com>
Date: Wed, 3 Jan 2024 11:46:17 +0000
Subject: [PATCH 056/178] a word

---
 docs/api/createSlice.mdx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/api/createSlice.mdx b/docs/api/createSlice.mdx
index 13720a6fd6..5621431fa7 100644
--- a/docs/api/createSlice.mdx
+++ b/docs/api/createSlice.mdx
@@ -809,7 +809,7 @@ context
 
 The Typescript system for custom slice creators uses a "creator registry" system similar to the module system for [RTK Query](/rtk-query/usage/customizing-create-api#creating-your-own-module).
 
-Creators are registered by using module augmentation to add a new key (their unique `type`) to the `SliceReducerCreators` interface. The interface receives three type parameters (`State`, `CaseReducers` and `Name`), and each entry should have use the `ReducerCreatorEntry` type utility.
+Creators are registered by using module augmentation to add a new key (their unique `type`) to the `SliceReducerCreators` interface. The interface receives three type parameters (`State`, `CaseReducers` and `Name`), and each entry should use the `ReducerCreatorEntry` type utility.
 
 ```ts no-transpile
 const reducerCreatorType = Symbol()

From 08f41ea812c0a5c0681330d7aa260cef17a1f00c Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.j.durrant@gmail.com>
Date: Wed, 3 Jan 2024 18:19:16 +0000
Subject: [PATCH 057/178] Use createAppSlice name consistently

---
 docs/api/createSlice.mdx                      | 39 ++++++++++++++-----
 .../toolkit/src/tests/createSlice.test.ts     | 28 ++++++-------
 2 files changed, 43 insertions(+), 24 deletions(-)

diff --git a/docs/api/createSlice.mdx b/docs/api/createSlice.mdx
index 5495f01ce4..5dd75b6cad 100644
--- a/docs/api/createSlice.mdx
+++ b/docs/api/createSlice.mdx
@@ -972,6 +972,25 @@ declare module '@reduxjs/toolkit' {
 
 TODO: blurb
 
+:::note `buildCreateSlice` usage
+
+For the sake of a complete example, most of the snippets below will include a `buildCreateSlice` call.
+
+In practicality, we expect that most apps will only call `buildCreateSlice` _once_, with as many creators as needed in that app.
+
+```ts no-transpile
+export const createAppSlice = buildCreateSlice({
+  creators: {
+    toaster: toastCreator,
+    paginationMethods: paginationCreator,
+    historyMethods: historyCreator,
+    undoable: undoableCreator,
+  },
+})
+```
+
+:::
+
 #### Single definitions
 
 Commonly, a creator will return a single reducer definition, to be handled by either itself or another creator.
@@ -1103,11 +1122,11 @@ const toastCreator: ReducerCreator<typeof toastCreatorType> = {
 function noop() {}
 
 // build the `createSlice` function
-const createToastSlice = buildCreateSlice({
+const createAppSlice = buildCreateSlice({
   creators: { toaster: toastCreator },
 })
 
-const toastSlice = createToastSlice({
+const toastSlice = createAppSlice({
   name: 'toast',
   initialState: {} as Record<string, Toast>,
   reducers: (create) => ({
@@ -1179,15 +1198,15 @@ const paginationCreator: ReducerCreator<typeof paginationCreatorType> = {
   },
 }
 
-const createPaginationSlice = buildCreateSlice({
-  creators: { paginationReducers: paginationCreator },
+const createAppSlice = buildCreateSlice({
+  creators: { paginationMethods: paginationCreator },
 })
 
-const paginationSlice = createPaginationSlice({
+const paginationSlice = createAppSlice({
   name: 'pagination',
   initialState: { page: 0, loading: false },
   reducers: (create) => ({
-    ...create.paginationReducers(),
+    ...create.paginationMethods(),
     toggleLoading: create.reducer((state) => {
       state.loading = !state.loading
     }),
@@ -1284,7 +1303,7 @@ const historyCreator: ReducerCreator<typeof historyCreatorType> = {
   },
 }
 
-const createHistorySlice = buildCreateSlice({
+const createAppSlice = buildCreateSlice({
   creators: { historyMethods: historyCreator },
 })
 
@@ -1296,7 +1315,7 @@ function getInitialHistoryState<T>(initialState: T): HistoryState<T> {
   }
 }
 
-const postSliceWithHistory = createHistorySlice({
+const postSliceWithHistory = createAppSlice({
   name: 'post',
   initialState: getInitialHistoryState({ title: '' }),
   reducers: (create) => ({
@@ -1398,11 +1417,11 @@ const undoableCreator: ReducerCreator<typeof undoableCreatorType> = {
   ),
 }
 
-const createHistorySlice = buildCreateSlice({
+const createAppSlice = buildCreateSlice({
   creators: { historyMethods: historyCreator, undoable: undoableCreator },
 })
 
-const postSliceWithHistory = createHistorySlice({
+const postSliceWithHistory = createAppSlice({
   name: 'post',
   initialState: getInitialHistoryState({ title: '', pinned: false }),
   reducers: (create) => ({
diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index f1674ee798..b8b9ba4088 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -914,11 +914,11 @@ describe('createSlice', () => {
       },
     }
     test('allows passing custom reducer creators, which can add actions and case reducers', () => {
-      const createLoaderSlice = buildCreateSlice({
+      const createAppSlice = buildCreateSlice({
         creators: { loader: loaderCreator },
       })
 
-      const loaderSlice = createLoaderSlice({
+      const loaderSlice = createAppSlice({
         name: 'loader',
         initialState: {} as Partial<Record<string, true>>,
         reducers: (create) => ({
@@ -1024,11 +1024,11 @@ describe('createSlice', () => {
     }
 
     test('condition creator', async () => {
-      const createConditionSlice = buildCreateSlice({
+      const createAppSlice = buildCreateSlice({
         creators: { condition: conditionCreator },
       })
 
-      const counterSlice = createConditionSlice({
+      const counterSlice = createAppSlice({
         name: 'counter',
         initialState: { value: 0 },
         reducers: (create) => ({
@@ -1088,11 +1088,11 @@ describe('createSlice', () => {
         },
       }
       test('fetch slice', () => {
-        const createFetchSlice = buildCreateSlice({
+        const createAppSlice = buildCreateSlice({
           creators: { fetchReducers: fetchCreator },
         })
 
-        const fetchSlice = createFetchSlice({
+        const fetchSlice = createAppSlice({
           name: 'fetch',
           initialState: { status: 'loading' } as FetchState<string>,
           reducers: (create) => ({
@@ -1129,11 +1129,11 @@ describe('createSlice', () => {
               }
             },
           }
-        const createPaginationSlice = buildCreateSlice({
+        const createAppSlice = buildCreateSlice({
           creators: { paginationReducers: paginationCreator },
         })
 
-        const paginationSlice = createPaginationSlice({
+        const paginationSlice = createAppSlice({
           name: 'pagination',
           initialState: {
             page: 1,
@@ -1259,14 +1259,14 @@ describe('createSlice', () => {
           ),
         }
 
-        const createHistorySlice = buildCreateSlice({
+        const createAppSlice = buildCreateSlice({
           creators: {
             historyMethods: historyMethodsCreator,
             undoable: undoableCreator,
           },
         })
 
-        const historySlice = createHistorySlice({
+        const historySlice = createAppSlice({
           name: 'history',
           initialState: getInitialHistoryState({ value: 1 }),
           reducers: (create) => ({
@@ -1336,10 +1336,10 @@ describe('createSlice', () => {
           handle() {},
         }
 
-        const createBatchSlice = buildCreateSlice({
+        const createAppSlice = buildCreateSlice({
           creators: { batchedReducer: batchedCreator },
         })
-        const counterSlice = createBatchSlice({
+        const counterSlice = createAppSlice({
           name: 'counter',
           initialState: { value: 0 },
           reducers: (create) => ({
@@ -1356,11 +1356,11 @@ describe('createSlice', () => {
         expect(incrementBy(1).meta).toEqual({ [SHOULD_AUTOBATCH]: true })
       })
       test.skip('creators can discourage their use if state is incompatible (types only)', () => {
-        const createFetchSlice = buildCreateSlice({
+        const createAppSlice = buildCreateSlice({
           creators: { fetchReducers: fetchCreator },
         })
 
-        const counterSlice = createFetchSlice({
+        const counterSlice = createAppSlice({
           name: 'counter',
           initialState: { value: 0 },
           reducers: (create) => ({

From f35359f49c70c0c0568caaa0e577a89113410307 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.j.durrant@gmail.com>
Date: Wed, 3 Jan 2024 18:26:40 +0000
Subject: [PATCH 058/178] Omit creator key entirely if value is never

---
 packages/toolkit/src/createSlice.ts            | 4 +++-
 packages/toolkit/src/tests/createSlice.test.ts | 2 +-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 9d91c19415..12cbbacd91 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -179,7 +179,9 @@ export type ReducerCreators<
   reducer: SliceReducerCreators<State>[ReducerType.reducer]['create']
   preparedReducer: SliceReducerCreators<State>[ReducerType.reducerWithPrepare]['create']
 } & {
-  [Name in keyof CreatorMap]: SliceReducerCreators<State>[CreatorMap[Name]]['create']
+  [Name in keyof CreatorMap as SliceReducerCreators<State>[CreatorMap[Name]]['create'] extends never
+    ? never
+    : Name]: SliceReducerCreators<State>[CreatorMap[Name]]['create']
 }
 
 interface ReducerHandlingContext<State> {
diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index b8b9ba4088..41e63a87e1 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -1364,7 +1364,7 @@ describe('createSlice', () => {
           name: 'counter',
           initialState: { value: 0 },
           reducers: (create) => ({
-            // @ts-expect-error
+            // @ts-expect-error incompatible state
             ...create.fetchReducers(),
           }),
         })

From 7798d7837a0a40ed31dff85e43734a1925bbd136 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.j.durrant@gmail.com>
Date: Wed, 3 Jan 2024 18:40:57 +0000
Subject: [PATCH 059/178] Add note about compatible state.

---
 docs/api/createSlice.mdx                      | 51 +++++++++++++++++++
 .../toolkit/src/tests/createSlice.test.ts     | 12 ++---
 2 files changed, 56 insertions(+), 7 deletions(-)

diff --git a/docs/api/createSlice.mdx b/docs/api/createSlice.mdx
index 5dd75b6cad..bf815162c2 100644
--- a/docs/api/createSlice.mdx
+++ b/docs/api/createSlice.mdx
@@ -872,6 +872,57 @@ The second argument to the `ReducerCreators` type is a map from creator names to
 
 :::
 
+:::note Ensuring compatible state
+
+Sometimes it's useful to have a reducer creator that only works with a specific state shape. You can ensure the creator is only callable if the state matches, using a conditional type:
+
+```ts no-transpile
+const loaderCreatorType = Symbol()
+
+declare module '@reduxjs/toolkit' {
+  export interface SliceReducerCreators<
+    State = any,
+    CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
+    Name extends string = string
+  > {
+    [loaderCreatorType]: ReducerCreatorEntry<
+      // highlight-next-line
+      State extends { loading: boolean }
+        ? () => {
+            start: CaseReducerDefinition<State, PayloadAction>
+            end: CaseReducerDefinition<State, PayloadAction>
+          }
+        : never
+    >
+  }
+}
+```
+
+Any creators that evaluate to the `never` type are omitted from the final `create` object.
+
+An alternative would be just using that required type _as_ the `State` type for the reducer definitions, so Typescript then complains when the creator is used.
+
+```ts no-transpile
+const loaderCreatorType = Symbol()
+
+declare module '@reduxjs/toolkit' {
+  export interface SliceReducerCreators<
+    State = any,
+    CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
+    Name extends string = string
+  > {
+    [loaderCreatorType]: ReducerCreatorEntry<
+      () => {
+        start: CaseReducerDefinition<{ loading: boolean }, PayloadAction>
+        end: CaseReducerDefinition<{ loading: boolean }, PayloadAction>
+      }
+    >
+  }
+}
+```
+
+:::
+
 #### `Exposes`
 
 The second type parameter for `ReducerCreatorEntry` is optional, but should be used if the creator will handle some reducer definitions itself. It indicates what actions and case reducers will be attached to the slice, and is used to determine the final types of `slice.actions` and `slice.caseReducers`.
diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index 41e63a87e1..4e9307f7c1 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -1482,13 +1482,11 @@ declare module '@reduxjs/toolkit' {
         : never
     >
     [paginationCreatorType]: ReducerCreatorEntry<
-      State extends PaginationState
-        ? (this: ReducerCreators<State>) => {
-            nextPage: CaseReducerDefinition<State, PayloadAction>
-            previousPage: CaseReducerDefinition<State, PayloadAction>
-            goToPage: CaseReducerDefinition<State, PayloadAction<number>>
-          }
-        : never
+      (this: ReducerCreators<State>) => {
+        nextPage: CaseReducerDefinition<PaginationState, PayloadAction>
+        previousPage: CaseReducerDefinition<PaginationState, PayloadAction>
+        goToPage: CaseReducerDefinition<PaginationState, PayloadAction<number>>
+      }
     >
     [historyMethodsCreatorType]: ReducerCreatorEntry<
       State extends HistoryState<unknown>

From 3d20781255d130808235b4fb04d0b46755ff7381 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Fri, 5 Jan 2024 21:28:36 +0000
Subject: [PATCH 060/178] Avoid using IfMaybeUndefined

---
 errors.json                         |  3 ++-
 packages/toolkit/src/createSlice.ts | 14 +++++++-------
 2 files changed, 9 insertions(+), 8 deletions(-)

diff --git a/errors.json b/errors.json
index 68aa8a4a7a..c5aa2dbe72 100644
--- a/errors.json
+++ b/errors.json
@@ -40,5 +40,6 @@
   "38": "Cannot refetch a query that has not been started yet.",
   "39": "Cannot use reserved creator name: name",
   "40": "Please use reducer creators passed to callback. Each reducer definition must have a `_reducerDefinitionType` property indicating which handler to use.",
-  "41": "Unsupported reducer type: type"
+  "41": "Unsupported reducer type: type",
+  "42": "Cannot use reserved creator type: "
 }
\ No newline at end of file
diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 12cbbacd91..65b4aacec2 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -55,12 +55,12 @@ export type ReducerCreatorEntry<
   } = {}
 > = {
   create: Create
-  actions: IfMaybeUndefined<Exposes['actions'], {}, Exposes['actions']>
-  caseReducers: IfMaybeUndefined<
-    Exposes['caseReducers'],
-    {},
-    Exposes['caseReducers']
-  >
+  actions: Exposes extends { actions: NonNullable<unknown> }
+    ? Exposes['actions']
+    : {}
+  caseReducers: Exposes extends { caseReducers: NonNullable<unknown> }
+    ? Exposes['caseReducers']
+    : {}
 }
 
 export interface SliceReducerCreators<
@@ -952,7 +952,7 @@ export function buildCreateSlice<
       creator.type === ReducerType.reducer ||
       creator.type === ReducerType.reducerWithPrepare
     ) {
-      throw new Error('Cannot use reserved creator type: ' + creator.type)
+      throw new Error(`Cannot use reserved creator type: ${creator.type}`)
     }
     creators[name] = creator.create
     if ('handle' in creator) {

From 139f750c828d7f03b01ba4f052d27eaf0706358d Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Fri, 5 Jan 2024 21:31:21 +0000
Subject: [PATCH 061/178] guard against symbol types

---
 packages/toolkit/src/createSlice.ts | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 65b4aacec2..ed763fa1f6 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -952,7 +952,9 @@ export function buildCreateSlice<
       creator.type === ReducerType.reducer ||
       creator.type === ReducerType.reducerWithPrepare
     ) {
-      throw new Error(`Cannot use reserved creator type: ${creator.type}`)
+      throw new Error(
+        `Cannot use reserved creator type: ${String(creator.type)}`
+      )
     }
     creators[name] = creator.create
     if ('handle' in creator) {

From 947de203ba94801bd31e8e6a1106f194f3d6dc47 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Fri, 5 Jan 2024 21:39:11 +0000
Subject: [PATCH 062/178] guard against more symbols

---
 errors.json                         | 4 ++--
 packages/toolkit/src/createSlice.ts | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/errors.json b/errors.json
index c5aa2dbe72..4d0120eec9 100644
--- a/errors.json
+++ b/errors.json
@@ -40,6 +40,6 @@
   "38": "Cannot refetch a query that has not been started yet.",
   "39": "Cannot use reserved creator name: name",
   "40": "Please use reducer creators passed to callback. Each reducer definition must have a `_reducerDefinitionType` property indicating which handler to use.",
-  "41": "Unsupported reducer type: type",
-  "42": "Cannot use reserved creator type: "
+  "41": "Cannot use reserved creator type: ",
+  "42": "Unsupported reducer type: "
 }
\ No newline at end of file
diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index ed763fa1f6..c00855d37c 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -1051,7 +1051,7 @@ export function buildCreateSlice<
         }
         const handler = handlers[type as RegisteredReducerType]
         if (!handler) {
-          throw new Error('Unsupported reducer type: ' + type)
+          throw new Error(`Unsupported reducer type: ${String(type)}`)
         }
         const reducerDetails: ReducerDetails = {
           reducerName,

From 2ef78e403e1a03b47b845f903172fed6f5f2b104 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sat, 6 Jan 2024 17:10:54 +0000
Subject: [PATCH 063/178] blurb it up

---
 docs/api/createSlice.mdx | 46 +++++++++++++++++++++++++++++++++++++---
 1 file changed, 43 insertions(+), 3 deletions(-)

diff --git a/docs/api/createSlice.mdx b/docs/api/createSlice.mdx
index bf815162c2..42dc1d989d 100644
--- a/docs/api/createSlice.mdx
+++ b/docs/api/createSlice.mdx
@@ -303,7 +303,7 @@ create.asyncThunk(
 
 :::note
 
-Typing for the `create.asyncThunk` works in the same way as [`createAsyncThunk`](../usage/usage-with-typescript#createasyncthunk), with one key difference.
+Typing for `create.asyncThunk` works in the same way as [`createAsyncThunk`](../usage/usage-with-typescript#createasyncthunk), with one key difference.
 
 A type for `state` and/or `dispatch` _cannot_ be provided as part of the `ThunkApiConfig`, as this would cause circular types.
 
@@ -668,7 +668,45 @@ store.dispatch(user.actions.setUserName('eric'))
 
 ## Custom creators
 
-TODO: a blurb introducing the idea of custom creators
+With the introduction of the [reducer "creator callback" syntax](#the-reducers-creator-callback-notation), the potential was there to allow other, more "custom", `create` methods.
+
+These "creators" would have the capability to:
+
+- Encapsulate reusable logic
+- Define multiple reducers at the same time
+- Modify slice behaviour by adding case/matcher reducers
+- Expose custom actions (thunks, for example) and case reducers
+
+```ts no-transpile
+const createAppSlice = buildCreateSlice({
+  creators: { historyMethods: historyCreator, undoable: undoableCreator },
+})
+
+const postSliceWithHistory = createAppSlice({
+  name: 'post',
+  initialState: getInitialHistoryState({ title: '', pinned: false }),
+  reducers: (create) => ({
+    ...create.historyMethods(),
+    updateTitle: create.preparedReducer(
+      create.undoable.withPayload<string>(),
+      create.undoable((state, action) => {
+        state.title = action.payload
+      })
+    ),
+    togglePinned: create.preparedReducer(
+      create.undoable.withoutPayload(),
+      create.undoable((state, action) => {
+        state.pinned = !state.pinned
+      })
+    ),
+  }),
+})
+
+const { undo, redo, reset, updateTitle, togglePinned } =
+  postSliceWithHistory.actions
+```
+
+In version TODO, this functionality was added. The below section will cover how to properly define and use these creators, and possible applications.
 
 ### Reducer definitions
 
@@ -1021,7 +1059,9 @@ declare module '@reduxjs/toolkit' {
 
 ### Further examples
 
-TODO: blurb
+This section will cover in depth examples, for potential applications with hopefully applicable lessons to other use cases.
+
+If you come up with a novel use for reducer creators, we'd love to hear it! It should even be possible to publish packages with creators for others to use.
 
 :::note `buildCreateSlice` usage
 

From a2660af09b9ea009a3829e2f368d02842f2ef114 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sat, 6 Jan 2024 23:27:53 +0000
Subject: [PATCH 064/178] slight tweak

---
 docs/api/createSlice.mdx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/api/createSlice.mdx b/docs/api/createSlice.mdx
index 42dc1d989d..a23feb4fd8 100644
--- a/docs/api/createSlice.mdx
+++ b/docs/api/createSlice.mdx
@@ -710,7 +710,7 @@ In version TODO, this functionality was added. The below section will cover how
 
 ### Reducer definitions
 
-A reducer definition is an object (or function) with a `_reducerDefinitionType` property indicating which creator should handle it. Other than this property, it is entirely up to you what this definition object can look like.
+A reducer definition is an object (or function) with a `_reducerDefinitionType` property indicating which creator should handle it. Other than this property, it is entirely up to the creator what this definition object can look like.
 
 For example, the `create.preparedReducer` creator uses a definition that looks like `{ prepare, reducer }`.
 

From 83985205950e98ae5c2a89d847bed783394592ec Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.durrant@marketdojo.com>
Date: Mon, 8 Jan 2024 11:47:26 +0000
Subject: [PATCH 065/178] remove extra tests now they're in the docs

---
 .../toolkit/src/tests/createSlice.test.ts     | 507 ++++--------------
 1 file changed, 93 insertions(+), 414 deletions(-)

diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index 90fd6a14e7..e4850bbc11 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -3,12 +3,10 @@ import type { Patch } from 'immer'
 import { applyPatches, enablePatches, produceWithPatches } from 'immer'
 import type {
   Action,
-  AnyListenerPredicate,
   CaseReducer,
   CaseReducerDefinition,
   PayloadAction,
   PayloadActionCreator,
-  PreparedCaseReducerDefinition,
   ReducerCreator,
   ReducerCreatorEntry,
   ReducerCreators,
@@ -17,8 +15,6 @@ import type {
   SliceActionType,
   SliceCaseReducers,
   ThunkAction,
-  UnknownAction,
-  UnsubscribeListener,
   WithSlice,
 } from '@reduxjs/toolkit'
 import {
@@ -30,13 +26,8 @@ import {
   createAction,
   isAnyOf,
   nanoid,
-  addListener,
-  createListenerMiddleware,
   createNextState,
   reducerCreator,
-  ReducerType,
-  prepareAutoBatched,
-  SHOULD_AUTOBATCH,
 } from '@reduxjs/toolkit'
 import {
   mockConsole,
@@ -49,12 +40,8 @@ enablePatches()
 type CreateSlice = typeof createSlice
 
 const loaderCreatorType = Symbol()
-const conditionCreatorType = Symbol()
-const fetchCreatorType = Symbol()
-const paginationCreatorType = Symbol()
 const historyMethodsCreatorType = Symbol()
 const undoableCreatorType = Symbol()
-const batchedCreatorType = Symbol()
 
 describe('createSlice', () => {
   let restore: () => void
@@ -627,7 +614,9 @@ describe('createSlice', () => {
           // @ts-expect-error asyncThunk not in creators
           reducers: (create) => ({ thunk: create.asyncThunk(() => {}) }),
         })
-      ).toThrowErrorMatchingInlineSnapshot(`[TypeError: create.asyncThunk is not a function]`)
+      ).toThrowErrorMatchingInlineSnapshot(
+        `[TypeError: create.asyncThunk is not a function]`
+      )
     })
     const createAppSlice = buildCreateSlice({
       creators: { asyncThunk: asyncThunkCreator },
@@ -864,7 +853,9 @@ describe('createSlice', () => {
             },
           }),
         })
-      ).toThrowErrorMatchingInlineSnapshot(`[Error: Please use reducer creators passed to callback. Each reducer definition must have a \`_reducerDefinitionType\` property indicating which handler to use.]`)
+      ).toThrowErrorMatchingInlineSnapshot(
+        `[Error: Please use reducer creators passed to callback. Each reducer definition must have a \`_reducerDefinitionType\` property indicating which handler to use.]`
+      )
     })
   })
   describe('custom slice reducer creators', () => {
@@ -963,305 +954,109 @@ describe('createSlice', () => {
       ])
     })
 
-    const conditionCreator: ReducerCreator<typeof conditionCreatorType> = {
-      type: conditionCreatorType,
-      create(makePredicate) {
-        return { _reducerDefinitionType: conditionCreatorType, makePredicate }
-      },
-      handle({ reducerName, type }, { makePredicate }, context) {
-        const trigger = createAction(type, (id: string, args: unknown[]) => ({
-          payload: id,
-          meta: { args },
-        }))
-        function thunkCreator(
-          timeout?: number,
-          ...args: any[]
-        ): ThunkAction<
-          Promise<boolean> & { unsubscribe?: UnsubscribeListener },
-          unknown,
-          unknown,
-          UnknownAction
-        > {
-          const predicate = makePredicate(...args)
-          return (dispatch) => {
-            const listenerId = nanoid()
-            let unsubscribe: UnsubscribeListener | undefined = undefined
-            const promise = new Promise<boolean>((resolve, reject) => {
-              unsubscribe = dispatch(
-                addListener({
-                  predicate: (action) =>
-                    trigger.match(action) && action.payload === listenerId,
-                  effect: (_, { condition, unsubscribe, signal }) => {
-                    signal.addEventListener('abort', () => reject(false))
-                    return condition(predicate, timeout)
-                      .then(resolve)
-                      .catch(reject)
-                      .finally(unsubscribe)
-                  },
-                })
-              ) as any
-              dispatch(trigger(listenerId, args))
-            })
-            return Object.assign(promise, { unsubscribe })
-          }
+    describe('creators can return multiple definitions to be spread, or something else entirely', () => {
+      function getInitialHistoryState<T>(initialState: T): HistoryState<T> {
+        return {
+          past: [],
+          present: initialState,
+          future: [],
         }
-        context.exposeAction(reducerName, thunkCreator)
-      },
-    }
-    function withStatus<P extends PromiseLike<any>>(promiseLike: P) {
-      const assigned = Object.assign(promiseLike, {
-        status: 'pending' as 'pending' | 'fulfilled' | 'rejected',
-      })
-      promiseLike.then(
-        () => (assigned.status = 'fulfilled'),
-        () => (assigned.status = 'rejected')
-      )
-      return assigned
-    }
-
-    test('condition creator', async () => {
-      const createAppSlice = buildCreateSlice({
-        creators: { condition: conditionCreator },
-      })
-
-      const counterSlice = createAppSlice({
-        name: 'counter',
-        initialState: { value: 0 },
-        reducers: (create) => ({
-          increment: create.reducer((state) => void state.value++),
-          waitUntilValue: create.condition(
-            (value: number) =>
-              (_action, state): boolean =>
-                counterSlice.selectors.selectValue(state as any) === value
-          ),
-        }),
-        selectors: {
-          selectValue: (state) => state.value,
-        },
-      })
-
-      const {
-        actions: { increment, waitUntilValue },
-        selectors: { selectValue },
-      } = counterSlice
-
-      const listener = createListenerMiddleware()
-
-      const reducer = combineSlices(counterSlice)
-
-      const store = configureStore({
-        reducer,
-        middleware: (gDM) => gDM().concat(listener.middleware),
-      })
-
-      const promise = withStatus(store.dispatch(waitUntilValue(undefined, 5)))
-      expect(promise).toEqual(expect.any(Promise))
-      expect(promise.unsubscribe).toEqual(expect.any(Function))
-
-      for (let i = 1; i <= 4; i++) {
-        store.dispatch(increment())
-        expect(selectValue(store.getState())).toBe(i)
-        expect(promise.status).toBe('pending')
       }
-      store.dispatch(increment())
-      expect(selectValue(store.getState())).toBe(5)
-      await expect(promise).resolves.toBe(true)
-      expect(promise.status).toBe('fulfilled')
-    })
-    describe('creators can return multiple definitions to be spread', () => {
-      const fetchCreator: ReducerCreator<typeof fetchCreatorType> = {
-        type: fetchCreatorType,
+      const historyMethodsCreator: ReducerCreator<
+        typeof historyMethodsCreatorType
+      > = {
+        type: historyMethodsCreatorType,
         create() {
           return {
-            start: this.reducer((state: FetchState<unknown>) => {
-              state.status = 'loading'
+            undo: this.reducer((state: HistoryState<unknown>) => {
+              const historyEntry = state.past.pop()
+              if (historyEntry) {
+                applyPatches(state, historyEntry.undo)
+                state.future.unshift(historyEntry)
+              }
             }),
-            success: this.reducer<any>((state: FetchState<unknown>, action) => {
-              state.data = action.payload
-              state.status = 'finished'
+            redo: this.reducer((state: HistoryState<unknown>) => {
+              const historyEntry = state.future.shift()
+              if (historyEntry) {
+                applyPatches(state, historyEntry.redo)
+                state.past.push(historyEntry)
+              }
             }),
+            reset: {
+              _reducerDefinitionType: historyMethodsCreatorType,
+              type: 'reset',
+            },
           }
         },
+        handle(details, definition, context) {
+          if (definition.type !== 'reset') {
+            throw new Error('unrecognised definition')
+          }
+          reducerCreator.handle(
+            details,
+            reducerCreator.create(() => context.getInitialState()),
+            context
+          )
+        },
       }
-      test('fetch slice', () => {
-        const createAppSlice = buildCreateSlice({
-          creators: { fetchReducers: fetchCreator },
-        })
 
-        const fetchSlice = createAppSlice({
-          name: 'fetch',
-          initialState: { status: 'loading' } as FetchState<string>,
-          reducers: (create) => ({
-            ...create.fetchReducers(),
-            magic: create.reducer((state) => {
-              state.status = 'finished'
-              state.data = 'hocus pocus'
-            }),
-          }),
-        })
-
-        const { start, success, magic } = fetchSlice.actions
-
-        expect(start).toEqual(expect.any(Function))
-        expect(start.type).toBe('fetch/start')
-      })
-      test('pagination slice', () => {
-        const paginationCreator: ReducerCreator<typeof paginationCreatorType> =
-          {
-            type: paginationCreatorType,
-            create() {
-              return {
-                nextPage: this.reducer((state: PaginationState) => {
-                  state.page++
-                }),
-                previousPage: this.reducer((state: PaginationState) => {
-                  state.page = Math.max(0, state.page - 1)
-                }),
-                goToPage: this.reducer<number>(
-                  (state: PaginationState, action) => {
-                    state.page = action.payload
+      const undoableCreator: ReducerCreator<typeof undoableCreatorType> = {
+        type: undoableCreatorType,
+        create: Object.assign(
+          function makeUndoable<A extends Action & { meta?: UndoableOptions }>(
+            reducer: CaseReducer<any, A>
+          ): CaseReducer<HistoryState<any>, A> {
+            return (state, action) => {
+              const [nextState, redoPatch, undoPatch] = produceWithPatches(
+                state,
+                (draft) => {
+                  const result = reducer(draft.present, action)
+                  if (typeof result !== 'undefined') {
+                    draft.present = result
                   }
-                ),
-              }
-            },
-          }
-        const createAppSlice = buildCreateSlice({
-          creators: { paginationReducers: paginationCreator },
-        })
-
-        const paginationSlice = createAppSlice({
-          name: 'pagination',
-          initialState: {
-            page: 1,
-            hasMoreData: true,
-          },
-          reducers: (create) => ({
-            ...create.paginationReducers(),
-            noMoreData: create.reducer((state) => {
-              state.hasMoreData = false
-            }),
-          }),
-          selectors: {
-            selectPage: (state) => state.page,
-            selectHasMoreData: (state) => state.hasMoreData,
-          },
-        })
-        const { nextPage, previousPage, goToPage } = paginationSlice.actions
-        const { selectHasMoreData, selectPage } = paginationSlice.selectors
-
-        const store = configureStore({
-          reducer: { [paginationSlice.reducerPath]: paginationSlice.reducer },
-        })
-
-        expect(selectPage(store.getState())).toBe(1)
-
-        store.dispatch(nextPage())
-
-        expect(selectPage(store.getState())).toBe(2)
-      })
-      test('history slice', () => {
-        function getInitialHistoryState<T>(initialState: T): HistoryState<T> {
-          return {
-            past: [],
-            present: initialState,
-            future: [],
-          }
-        }
-        const historyMethodsCreator: ReducerCreator<
-          typeof historyMethodsCreatorType
-        > = {
-          type: historyMethodsCreatorType,
-          create() {
-            return {
-              undo: this.reducer((state: HistoryState<unknown>) => {
-                const historyEntry = state.past.pop()
-                if (historyEntry) {
-                  applyPatches(state, historyEntry.undo)
-                  state.future.unshift(historyEntry)
                 }
-              }),
-              redo: this.reducer((state: HistoryState<unknown>) => {
-                const historyEntry = state.future.shift()
-                if (historyEntry) {
-                  applyPatches(state, historyEntry.redo)
-                  state.past.push(historyEntry)
-                }
-              }),
-              reset: {
-                _reducerDefinitionType: historyMethodsCreatorType,
-                type: 'reset',
-              },
-            }
-          },
-          handle(details, definition, context) {
-            if (definition.type !== 'reset') {
-              throw new Error('unrecognised definition')
-            }
-            reducerCreator.handle(
-              details,
-              reducerCreator.create(() => context.getInitialState()),
-              context
-            )
-          },
-        }
-
-        const undoableCreator: ReducerCreator<typeof undoableCreatorType> = {
-          type: undoableCreatorType,
-          create: Object.assign(
-            function makeUndoable<
-              A extends Action & { meta?: UndoableOptions }
-            >(reducer: CaseReducer<any, A>): CaseReducer<HistoryState<any>, A> {
-              return (state, action) => {
-                const [nextState, redoPatch, undoPatch] = produceWithPatches(
-                  state,
-                  (draft) => {
-                    const result = reducer(draft.present, action)
-                    if (typeof result !== 'undefined') {
-                      draft.present = result
-                    }
-                  }
-                )
-                let finalState = nextState
-                const undoable = action.meta?.undoable ?? true
-                if (undoable) {
-                  finalState = createNextState(finalState, (draft) => {
-                    draft.past.push({
-                      undo: undoPatch,
-                      redo: redoPatch,
-                    })
-                    draft.future = []
+              )
+              let finalState = nextState
+              const undoable = action.meta?.undoable ?? true
+              if (undoable) {
+                finalState = createNextState(finalState, (draft) => {
+                  draft.past.push({
+                    undo: undoPatch,
+                    redo: redoPatch,
                   })
-                }
-                return finalState
-              }
-            },
-            {
-              withoutPayload() {
-                return (options?: UndoableOptions) => ({
-                  payload: undefined,
-                  meta: options,
+                  draft.future = []
                 })
-              },
-              withPayload<P>() {
-                return (
-                  ...[payload, options]: IfMaybeUndefined<
-                    P,
-                    [payload?: P, options?: UndoableOptions],
-                    [payload: P, options?: UndoableOptions]
-                  >
-                ) => ({ payload: payload as P, meta: options })
-              },
+              }
+              return finalState
             }
-          ),
-        }
-
-        const createAppSlice = buildCreateSlice({
-          creators: {
-            historyMethods: historyMethodsCreator,
-            undoable: undoableCreator,
           },
-        })
+          {
+            withoutPayload() {
+              return (options?: UndoableOptions) => ({
+                payload: undefined,
+                meta: options,
+              })
+            },
+            withPayload<P>() {
+              return (
+                ...[payload, options]: IfMaybeUndefined<
+                  P,
+                  [payload?: P, options?: UndoableOptions],
+                  [payload: P, options?: UndoableOptions]
+                >
+              ) => ({ payload: payload as P, meta: options })
+            },
+          }
+        ),
+      }
 
+      const createAppSlice = buildCreateSlice({
+        creators: {
+          historyMethods: historyMethodsCreator,
+          undoable: undoableCreator,
+        },
+      })
+      test('history slice', () => {
         const historySlice = createAppSlice({
           name: 'history',
           initialState: getInitialHistoryState({ value: 1 }),
@@ -1313,55 +1108,13 @@ describe('createSlice', () => {
         store.dispatch(reset())
         expect(selectValue(store.getState())).toBe(1)
       })
-      test('batchable', () => {
-        const batchedCreator: ReducerCreator<typeof batchedCreatorType> = {
-          type: batchedCreatorType,
-          create: Object.assign(
-            function (
-              this: ReducerCreators<any>,
-              reducer: CaseReducer<any, any>
-            ) {
-              return this.preparedReducer(prepareAutoBatched(), reducer) as any
-            },
-            {
-              test() {
-                return { _reducerDefinitionType: batchedCreatorType } as const
-              },
-            }
-          ),
-          handle() {},
-        }
-
-        const createAppSlice = buildCreateSlice({
-          creators: { batchedReducer: batchedCreator },
-        })
-        const counterSlice = createAppSlice({
-          name: 'counter',
-          initialState: { value: 0 },
-          reducers: (create) => ({
-            increment: create.batchedReducer((state) => {
-              state.value++
-            }),
-            incrementBy: create.batchedReducer<number>((state, { payload }) => {
-              state.value += payload
-            }),
-          }),
-        })
-        const { increment, incrementBy } = counterSlice.actions
-        expect(increment().meta).toEqual({ [SHOULD_AUTOBATCH]: true })
-        expect(incrementBy(1).meta).toEqual({ [SHOULD_AUTOBATCH]: true })
-      })
       test.skip('creators can discourage their use if state is incompatible (types only)', () => {
-        const createAppSlice = buildCreateSlice({
-          creators: { fetchReducers: fetchCreator },
-        })
-
         const counterSlice = createAppSlice({
           name: 'counter',
           initialState: { value: 0 },
           reducers: (create) => ({
             // @ts-expect-error incompatible state
-            ...create.fetchReducers(),
+            ...create.historyMethods(),
           }),
         })
       })
@@ -1375,15 +1128,6 @@ interface LoaderReducerDefinition<State>
   ended?: CaseReducer<State, PayloadAction<string>>
 }
 
-interface FetchState<T> {
-  data?: T
-  status: 'loading' | 'finished' | 'error'
-}
-
-interface PaginationState {
-  page: number
-}
-
 interface PatchesState {
   undo: Patch[]
   redo: Patch[]
@@ -1440,50 +1184,6 @@ declare module '@reduxjs/toolkit' {
         }
       }
     >
-    [conditionCreatorType]: ReducerCreatorEntry<
-      <Args extends any[]>(
-        makePredicate: (...args: Args) => AnyListenerPredicate<unknown>
-      ) => ReducerDefinition<typeof conditionCreatorType> & {
-        makePredicate: (...args: Args) => AnyListenerPredicate<unknown>
-      },
-      {
-        actions: {
-          [ReducerName in ReducerNamesOfType<
-            CaseReducers,
-            typeof conditionCreatorType
-          >]: CaseReducers[ReducerName] extends {
-            makePredicate: (
-              ...args: infer Args
-            ) => AnyListenerPredicate<unknown>
-          }
-            ? (
-                timeout?: number,
-                ...args: Args
-              ) => ThunkAction<
-                Promise<boolean> & { unsubscribe?: UnsubscribeListener },
-                unknown,
-                unknown,
-                UnknownAction
-              >
-            : never
-        }
-      }
-    >
-    [fetchCreatorType]: ReducerCreatorEntry<
-      State extends FetchState<infer Data>
-        ? (this: ReducerCreators<State>) => {
-            start: CaseReducerDefinition<State, PayloadAction>
-            success: CaseReducerDefinition<State, PayloadAction<Data>>
-          }
-        : never
-    >
-    [paginationCreatorType]: ReducerCreatorEntry<
-      (this: ReducerCreators<State>) => {
-        nextPage: CaseReducerDefinition<PaginationState, PayloadAction>
-        previousPage: CaseReducerDefinition<PaginationState, PayloadAction>
-        goToPage: CaseReducerDefinition<PaginationState, PayloadAction<number>>
-      }
-    >
     [historyMethodsCreatorType]: ReducerCreatorEntry<
       State extends HistoryState<unknown>
         ? (this: ReducerCreators<State>) => {
@@ -1534,26 +1234,5 @@ declare module '@reduxjs/toolkit' {
           }
         : never
     >
-    [batchedCreatorType]: ReducerCreatorEntry<{
-      (
-        this: ReducerCreators<State>,
-        reducer: CaseReducer<State, PayloadAction>
-      ): PreparedCaseReducerDefinition<
-        State,
-        () => { payload: undefined; meta: unknown }
-      >
-      <Payload>(
-        this: ReducerCreators<State>,
-        reducer: CaseReducer<State, PayloadAction<Payload>>
-      ): PreparedCaseReducerDefinition<
-        State,
-        IfMaybeUndefined<
-          Payload,
-          (payload?: Payload) => { payload: Payload; meta: unknown },
-          (payload: Payload) => { payload: Payload; meta: unknown }
-        >
-      >
-      test(): ReducerDefinition<typeof batchedCreatorType>
-    }>
   }
 }

From 45d765b800b6e6abadf4d207c619712ebad06a15 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Wed, 10 Jan 2024 21:46:54 +0000
Subject: [PATCH 066/178] provide more context on buildCreateSlice

---
 docs/api/createSlice.mdx | 47 ++++++++++++++++++++++++++++++++++++----
 1 file changed, 43 insertions(+), 4 deletions(-)

diff --git a/docs/api/createSlice.mdx b/docs/api/createSlice.mdx
index a23feb4fd8..a7e9d77946 100644
--- a/docs/api/createSlice.mdx
+++ b/docs/api/createSlice.mdx
@@ -250,7 +250,7 @@ To avoid pulling `createAsyncThunk` into the bundle size of `createSlice` by def
 
 The version of `createSlice` exported from RTK will not include an `asyncThunk` property on the `create` object.
 
-Instead, import `buildCreateSlice` and `asyncThunkCreator`, and create your own version of `createSlice`:
+Instead, import [`buildCreateSlice`](#buildcreateslice) and `asyncThunkCreator`, and create your own version of `createSlice`:
 
 ```ts
 import { buildCreateSlice, asyncThunkCreator } from '@reduxjs/toolkit'
@@ -599,6 +599,25 @@ const { selectValue } = counterSlice.getSelectors(
 
 :::
 
+## `buildCreateSlice`
+
+`buildCreateSlice` allows you to create a custom version of `createSlice` with some configuration.
+
+Currently, this is only used for [custom creators](#custom-creators).
+
+### Parameters
+
+`buildCreateSlice` accepts a single configuration object parameter, with the following options:
+
+```ts no-transpile
+function buildCreateSlice({
+  // An map of creators to names. The names will used when constructing the `create` object passed to `reducers`.
+  creators?: Record<string, ReducerCreator>
+})
+```
+
+It returns an instance of [`createSlice`](#).
+
 ## Examples
 
 ```ts
@@ -743,7 +762,29 @@ Typically a creator will return a [single reducer definition](#single-definition
 
 A creator definition is an object with a `type` property, a `create` method, and an optional `handle` method.
 
-The `type` property should be the same constant used for reducer definitions to be handled by this creator. To avoid collision, we recommend using Symbols for this. It's also used for defining/retrieving types - see [Typescript](#typescript).
+It's passed to [`buildCreateSlice`](#buildcreateslice) as part of the `creators` object, and the name the creator is nested under in the `create` object is taken from the `buildCreateSlice` call.
+
+```ts no-transpile
+import { buildCreateSlice } from '@reduxjs/toolkit'
+
+const createAppSlice = buildCreateSlice({
+  // highlight-next-line
+  creators: { batchable: batchableCreator },
+})
+
+const todoSlice = createSlice({
+  name: 'todos',
+  initialState: [] as Todo[],
+  reducers: (create) => ({
+    // highlight-next-line
+    addTodo: create.batchable<Todo>((state, action) => {
+      state.push(action.payload)
+    }),
+  }),
+})
+```
+
+The `type` property of the definition should be the same constant used for reducer definitions to be handled by this creator. To avoid collision, we recommend using Symbols for this. It's also used for defining/retrieving types - see [Typescript](#typescript).
 
 ```ts no-transpile
 const reducerCreatorType = Symbol()
@@ -773,8 +814,6 @@ The `create` method is the function that will be attached to the `create` object
 
 Because it's a function, the `this` value will be the final `create` object when called (assuming a `create.creator()` call). It also could have additional methods attached.
 
-The actual name the creator will be nested under is taken from the `buildCreateSlice` call. For example, `buildCreateSlice({ creators: { asyncThunk: asyncThunkCreator } })` results in the creator being available as `create.asyncThunk`.
-
 See the [Further examples](#further-examples) section for some examples of these.
 
 #### `handle`

From eacd62167feea69f7e1f7b2b9e040495f81695ac Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sat, 13 Jan 2024 21:34:14 +0000
Subject: [PATCH 067/178] move buildCreateSlice after examples

---
 docs/api/createSlice.mdx | 38 +++++++++++++++++++-------------------
 1 file changed, 19 insertions(+), 19 deletions(-)

diff --git a/docs/api/createSlice.mdx b/docs/api/createSlice.mdx
index a7e9d77946..c805559808 100644
--- a/docs/api/createSlice.mdx
+++ b/docs/api/createSlice.mdx
@@ -599,25 +599,6 @@ const { selectValue } = counterSlice.getSelectors(
 
 :::
 
-## `buildCreateSlice`
-
-`buildCreateSlice` allows you to create a custom version of `createSlice` with some configuration.
-
-Currently, this is only used for [custom creators](#custom-creators).
-
-### Parameters
-
-`buildCreateSlice` accepts a single configuration object parameter, with the following options:
-
-```ts no-transpile
-function buildCreateSlice({
-  // An map of creators to names. The names will used when constructing the `create` object passed to `reducers`.
-  creators?: Record<string, ReducerCreator>
-})
-```
-
-It returns an instance of [`createSlice`](#).
-
 ## Examples
 
 ```ts
@@ -685,6 +666,25 @@ store.dispatch(user.actions.setUserName('eric'))
 // -> { counter: 12, user: { name: 'eric', age: 22} }
 ```
 
+## `buildCreateSlice`
+
+`buildCreateSlice` allows you to create a custom version of `createSlice` with some configuration.
+
+Currently, this is only used for [custom creators](#custom-creators).
+
+### Parameters
+
+`buildCreateSlice` accepts a single configuration object parameter, with the following options:
+
+```ts no-transpile
+function buildCreateSlice({
+  // An map of creators to names. The names will used when constructing the `create` object passed to `reducers`.
+  creators?: Record<string, ReducerCreator>
+})
+```
+
+It returns an instance of [`createSlice`](#).
+
 ## Custom creators
 
 With the introduction of the [reducer "creator callback" syntax](#the-reducers-creator-callback-notation), the potential was there to allow other, more "custom", `create` methods.

From 1c562a77ceba84bb0ebc501d0192b9ee8a71e768 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Wed, 17 Jan 2024 23:31:22 +0000
Subject: [PATCH 068/178] begin restructuring

---
 docs/api/createSlice.mdx | 447 ++++++++++++++++++++++-----------------
 1 file changed, 254 insertions(+), 193 deletions(-)

diff --git a/docs/api/createSlice.mdx b/docs/api/createSlice.mdx
index c805559808..dd26655891 100644
--- a/docs/api/createSlice.mdx
+++ b/docs/api/createSlice.mdx
@@ -134,16 +134,16 @@ const todosSlice = createSlice({
 })
 ```
 
-### The `reducers` "creator callback" notation
+:::tip Slice creators and the `reducers` "creator callback" notation
 
-Alternatively, the `reducers` field can be a callback which receives a "create" object.
+The `reducers` field can also be a callback which receives a "create" object. This allows you to use [slice creators](#slice-creators) with features such as [async thunks](./createAsyncThunk) as part of your slice.
 
-The main benefit of this is that you can use [custom creators](#custom-creators) such as [async thunks](./createAsyncThunk) as part of your slice (though for bundle size reasons, you [need a bit of setup for this](#createasyncthunk)). Types are also slightly simplified for prepared reducers.
+This is fully covered in the [slice creators](#slice-creators) section, but here's a quick example:
 
 ```ts title="Creator callback for reducers"
 import { buildCreateSlice, asyncThunkCreator, nanoid } from '@reduxjs/toolkit'
 
-const createSlice = buildCreateSlice({
+const createAppSlice = buildCreateSlice({
   creators: { asyncThunk: asyncThunkCreator },
 })
 
@@ -157,199 +157,40 @@ interface TodoState {
   todos: Item[]
 }
 
-const todosSlice = createSlice({
+const todosSlice = createAppSlice({
   name: 'todos',
   initialState: {
     loading: false,
     todos: [],
   } as TodoState,
   reducers: (create) => ({
-    deleteTodo: create.reducer<number>((state, action) => {
-      state.todos.splice(action.payload, 1)
-    }),
-    addTodo: create.preparedReducer(
-      (text: string) => {
-        const id = nanoid()
-        return { payload: { id, text } }
-      },
-      // action type is inferred from prepare callback
-      (state, action) => {
-        state.todos.push(action.payload)
-      }
-    ),
     fetchTodo: create.asyncThunk(
+      // payload creator
       async (id: string, thunkApi) => {
         const res = await fetch(`myApi/todos?id=${id}`)
         return (await res.json()) as Item
       },
       {
+        // reducers for lifecycle
         pending: (state) => {
           state.loading = true
         },
-        rejected: (state, action) => {
-          state.loading = false
-        },
         fulfilled: (state, action) => {
-          state.loading = false
           state.todos.push(action.payload)
         },
+        settled: (state, action) => {
+          state.loading = false
+        },
+        // additional options
+        options: {
+          idGenerator: () => nanoid(30),
+        },
       }
     ),
   }),
 })
 
-export const { addTodo, deleteTodo, fetchTodo } = todosSlice.actions
-```
-
-#### Create Methods
-
-#### `create.reducer`
-
-A standard slice case reducer.
-
-**Parameters**
-
-- **reducer** The slice case reducer to use.
-
-```ts no-transpile
-create.reducer<Todo>((state, action) => {
-  state.todos.push(action.payload)
-})
-```
-
-#### `create.preparedReducer`
-
-A [prepared](#customizing-generated-action-creators) reducer, to customize the action creator.
-
-**Parameters**
-
-- **prepareAction** The [`prepare callback`](./createAction#using-prepare-callbacks-to-customize-action-contents).
-- **reducer** The slice case reducer to use.
-
-The action passed to the case reducer will be inferred from the prepare callback's return.
-
-```ts no-transpile
-create.preparedReducer(
-  (text: string) => {
-    const id = nanoid()
-    return { payload: { id, text } }
-  },
-  (state, action) => {
-    state.todos.push(action.payload)
-  }
-)
-```
-
-#### `create.asyncThunk`
-
-Creates an async thunk instead of an action creator.
-
-:::warning Setup
-
-To avoid pulling `createAsyncThunk` into the bundle size of `createSlice` by default, some extra setup is required to use `create.asyncThunk`.
-
-The version of `createSlice` exported from RTK will not include an `asyncThunk` property on the `create` object.
-
-Instead, import [`buildCreateSlice`](#buildcreateslice) and `asyncThunkCreator`, and create your own version of `createSlice`:
-
-```ts
-import { buildCreateSlice, asyncThunkCreator } from '@reduxjs/toolkit'
-
-export const createAppSlice = buildCreateSlice({
-  creators: { asyncThunk: asyncThunkCreator },
-})
-```
-
-Then import this `createAppSlice` as needed instead of the exported version from RTK.
-
-:::
-
-**Parameters**
-
-- **payloadCreator** The thunk [payload creator](./createAsyncThunk#payloadcreator).
-- **config** The configuration object. (optional)
-
-The configuration object can contain case reducers for each of the [lifecycle actions](./createAsyncThunk#promise-lifecycle-actions) (`pending`, `fulfilled`, and `rejected`), as well as a `settled` reducer that will run for both fulfilled and rejected actions (note that this will run _after_ any provided `fulfilled`/`rejected` reducers. Conceptually it can be thought of like a `finally` block.).
-
-Each case reducer will be attached to the slice's `caseReducers` object, e.g. `slice.caseReducers.fetchTodo.fulfilled`.
-
-The configuration object can also contain [`options`](./createAsyncThunk#options).
-
-```ts no-transpile
-create.asyncThunk(
-  async (id: string, thunkApi) => {
-    const res = await fetch(`myApi/todos?id=${id}`)
-    return (await res.json()) as Item
-  },
-  {
-    pending: (state) => {
-      state.loading = true
-    },
-    rejected: (state, action) => {
-      state.error = action.payload ?? action.error
-    },
-    fulfilled: (state, action) => {
-      state.todos.push(action.payload)
-    },
-    settled: (state, action) => {
-      state.loading = false
-    }
-    options: {
-      idGenerator: uuid,
-    },
-  }
-)
-```
-
-:::note
-
-Typing for `create.asyncThunk` works in the same way as [`createAsyncThunk`](../usage/usage-with-typescript#createasyncthunk), with one key difference.
-
-A type for `state` and/or `dispatch` _cannot_ be provided as part of the `ThunkApiConfig`, as this would cause circular types.
-
-Instead, it is necessary to assert the type when needed - `getState() as RootState`. You may also include an explicit return type for the payload function as well, in order to break the circular type inference cycle.
-
-```ts no-transpile
-create.asyncThunk<Todo, string, { rejectValue: { error: string } }>(
-  // highlight-start
-  // may need to include an explicit return type
-  async (id: string, thunkApi): Promise<Todo> => {
-    // Cast types for `getState` and `dispatch` manually
-    const state = thunkApi.getState() as RootState
-    const dispatch = thunkApi.dispatch as AppDispatch
-    // highlight-end
-    try {
-      const todo = await fetchTodo()
-      return todo
-    } catch (e) {
-      throw thunkApi.rejectWithValue({
-        error: 'Oh no!',
-      })
-    }
-  }
-)
-```
-
-For common thunk API configuration options, a [`withTypes` helper](../usage/usage-with-typescript#defining-a-pre-typed-createasyncthunk) is provided:
-
-```ts no-transpile
-reducers: (create) => {
-  const createAThunk =
-    create.asyncThunk.withTypes<{ rejectValue: { error: string } }>()
-
-  return {
-    fetchTodo: createAThunk<Todo, string>(async (id, thunkApi) => {
-      throw thunkApi.rejectWithValue({
-        error: 'Oh no!',
-      })
-    }),
-    fetchTodos: createAThunk<Todo[], string>(async (id, thunkApi) => {
-      throw thunkApi.rejectWithValue({
-        error: 'Oh no, not again!',
-      })
-    }),
-  }
-}
+export const { fetchTodo } = todosSlice.actions
 ```
 
 :::
@@ -670,7 +511,7 @@ store.dispatch(user.actions.setUserName('eric'))
 
 `buildCreateSlice` allows you to create a custom version of `createSlice` with some configuration.
 
-Currently, this is only used for [custom creators](#custom-creators).
+Currently, this is only used for [slice creators](#slice-reducer-creators).
 
 ### Parameters
 
@@ -685,7 +526,7 @@ function buildCreateSlice({
 
 It returns an instance of [`createSlice`](#).
 
-## Custom creators
+## Slice creators
 
 With the introduction of the [reducer "creator callback" syntax](#the-reducers-creator-callback-notation), the potential was there to allow other, more "custom", `create` methods.
 
@@ -727,7 +568,227 @@ const { undo, redo, reset, updateTitle, togglePinned } =
 
 In version TODO, this functionality was added. The below section will cover how to properly define and use these creators, and possible applications.
 
-### Reducer definitions
+### The `reducers` "creator callback" notation
+
+In order to use slice creators, `reducers` should be a callback function. This callback will receive a `create` object, which contains the creators passed to [`buildCreateSlice`](#buildcreateslice), along with a couple of inbuilt creators.
+
+```ts title="Creator callback for reducers"
+import { buildCreateSlice, asyncThunkCreator, nanoid } from '@reduxjs/toolkit'
+
+const createAppSlice = buildCreateSlice({
+  creators: { asyncThunk: asyncThunkCreator },
+})
+
+interface Item {
+  id: string
+  text: string
+}
+
+interface TodoState {
+  loading: boolean
+  todos: Item[]
+}
+
+const todosSlice = createAppSlice({
+  name: 'todos',
+  initialState: {
+    loading: false,
+    todos: [],
+  } as TodoState,
+  reducers: (create) => ({
+    deleteTodo: create.reducer<number>((state, action) => {
+      state.todos.splice(action.payload, 1)
+    }),
+    addTodo: create.preparedReducer(
+      (text: string) => {
+        const id = nanoid()
+        return { payload: { id, text } }
+      },
+      // action type is inferred from prepare callback
+      (state, action) => {
+        state.todos.push(action.payload)
+      }
+    ),
+    fetchTodo: create.asyncThunk(
+      async (id: string, thunkApi) => {
+        const res = await fetch(`myApi/todos?id=${id}`)
+        return (await res.json()) as Item
+      },
+      {
+        pending: (state) => {
+          state.loading = true
+        },
+        rejected: (state, action) => {
+          state.loading = false
+        },
+        fulfilled: (state, action) => {
+          state.loading = false
+          state.todos.push(action.payload)
+        },
+      }
+    ),
+  }),
+})
+
+export const { addTodo, deleteTodo, fetchTodo } = todosSlice.actions
+```
+
+#### RTK Creators
+
+##### `create.reducer`
+
+A standard slice case reducer.
+
+**Parameters**
+
+- **reducer** The slice case reducer to use.
+
+```ts no-transpile
+create.reducer<Todo>((state, action) => {
+  state.todos.push(action.payload)
+})
+```
+
+##### `create.preparedReducer`
+
+A [prepared](#customizing-generated-action-creators) reducer, to customize the action creator.
+
+**Parameters**
+
+- **prepareAction** The [`prepare callback`](./createAction#using-prepare-callbacks-to-customize-action-contents).
+- **reducer** The slice case reducer to use.
+
+The action passed to the case reducer will be inferred from the prepare callback's return.
+
+```ts no-transpile
+create.preparedReducer(
+  (text: string) => {
+    const id = nanoid()
+    return { payload: { id, text } }
+  },
+  (state, action) => {
+    state.todos.push(action.payload)
+  }
+)
+```
+
+##### `create.asyncThunk`
+
+Creates an async thunk instead of an action creator.
+
+:::warning Setup
+
+To avoid pulling `createAsyncThunk` into the bundle size of `createSlice` by default, the `asyncThunk` creator is not included in the default `createSlice` instance.
+
+This means it will not include an `asyncThunk` property on the `create` object.
+
+Instead, import [`buildCreateSlice`](#buildcreateslice) and `asyncThunkCreator`, and create your own version of `createSlice`:
+
+```ts
+import { buildCreateSlice, asyncThunkCreator } from '@reduxjs/toolkit'
+
+export const createAppSlice = buildCreateSlice({
+  creators: { asyncThunk: asyncThunkCreator },
+})
+```
+
+Then import this `createAppSlice` as needed instead of the exported version from RTK.
+
+:::
+
+**Parameters**
+
+- **payloadCreator** The thunk [payload creator](./createAsyncThunk#payloadcreator).
+- **config** The configuration object. (optional)
+
+The configuration object can contain case reducers for each of the [lifecycle actions](./createAsyncThunk#promise-lifecycle-actions) (`pending`, `fulfilled`, and `rejected`), as well as a `settled` reducer that will run for both fulfilled and rejected actions (note that this will run _after_ any provided `fulfilled`/`rejected` reducers. Conceptually it can be thought of like a `finally` block.).
+
+Each case reducer will be attached to the slice's `caseReducers` object, e.g. `slice.caseReducers.fetchTodo.fulfilled`.
+
+The configuration object can also contain [`options`](./createAsyncThunk#options).
+
+```ts no-transpile
+create.asyncThunk(
+  async (id: string, thunkApi) => {
+    const res = await fetch(`myApi/todos?id=${id}`)
+    return (await res.json()) as Item
+  },
+  {
+    pending: (state) => {
+      state.loading = true
+    },
+    rejected: (state, action) => {
+      state.error = action.payload ?? action.error
+    },
+    fulfilled: (state, action) => {
+      state.todos.push(action.payload)
+    },
+    settled: (state, action) => {
+      state.loading = false
+    }
+    options: {
+      idGenerator: uuid,
+    },
+  }
+)
+```
+
+:::note
+
+Typing for `create.asyncThunk` works in the same way as [`createAsyncThunk`](../usage/usage-with-typescript#createasyncthunk), with one key difference.
+
+A type for `state` and/or `dispatch` _cannot_ be provided as part of the `ThunkApiConfig`, as this would cause circular types.
+
+Instead, it is necessary to assert the type when needed - `getState() as RootState`. You may also include an explicit return type for the payload function as well, in order to break the circular type inference cycle.
+
+```ts no-transpile
+create.asyncThunk<Todo, string, { rejectValue: { error: string } }>(
+  // highlight-start
+  // may need to include an explicit return type
+  async (id: string, thunkApi): Promise<Todo> => {
+    // Cast types for `getState` and `dispatch` manually
+    const state = thunkApi.getState() as RootState
+    const dispatch = thunkApi.dispatch as AppDispatch
+    // highlight-end
+    try {
+      const todo = await fetchTodo()
+      return todo
+    } catch (e) {
+      throw thunkApi.rejectWithValue({
+        error: 'Oh no!',
+      })
+    }
+  }
+)
+```
+
+For common thunk API configuration options, a [`withTypes` helper](../usage/usage-with-typescript#defining-a-pre-typed-createasyncthunk) is provided:
+
+```ts no-transpile
+reducers: (create) => {
+  const createAThunk =
+    create.asyncThunk.withTypes<{ rejectValue: { error: string } }>()
+
+  return {
+    fetchTodo: createAThunk<Todo, string>(async (id, thunkApi) => {
+      throw thunkApi.rejectWithValue({
+        error: 'Oh no!',
+      })
+    }),
+    fetchTodos: createAThunk<Todo[], string>(async (id, thunkApi) => {
+      throw thunkApi.rejectWithValue({
+        error: 'Oh no, not again!',
+      })
+    }),
+  }
+}
+```
+
+:::
+
+### Writing your own creators
+
+#### Reducer definitions
 
 A reducer definition is an object (or function) with a `_reducerDefinitionType` property indicating which creator should handle it. Other than this property, it is entirely up to the creator what this definition object can look like.
 
@@ -758,7 +819,7 @@ const definitions = {
 
 Typically a creator will return a [single reducer definition](#single-definitions), but it could return an object of [multiple definitions](#multiple-definitions) to be spread into the final object, or [something else entirely](#other)!
 
-### Creator definitions
+#### Creator definitions
 
 A creator definition is an object with a `type` property, a `create` method, and an optional `handle` method.
 
@@ -808,7 +869,7 @@ const reducerCreator: ReducerCreator<typeof reducerCreatorType> = {
 }
 ```
 
-#### `create`
+##### `create`
 
 The `create` method is the function that will be attached to the `create` object, before it's passed to the `reducers` callback.
 
@@ -833,7 +894,7 @@ The reducer details object has two properties:
 
 The context object includes:
 
-#### `addCase`
+##### `addCase`
 
 The same as [`addCase`](./createReducer#builderaddcase) for `createReducer` and `extraReducers`. Adds a case reducer for a given action type, and can receive an action type string or an action creator with a `.type` property.
 
@@ -842,7 +903,7 @@ const action = createAction(type)
 context.addCase(action, reducer)
 ```
 
-#### `addMatcher`
+##### `addMatcher`
 
 The same as [`addMatcher`](./createReducer#builderaddmatcher) for `createReducer` and `extraReducers`. Adds a case reducer which will be called when a given matcher returns true.
 
@@ -851,7 +912,7 @@ const matcher = isAnyOf(action, action2)
 context.addMatcher(matcher, reducer)
 ```
 
-#### `exposeAction`
+##### `exposeAction`
 
 Attaches a value to `slice.actions`. Receives the key to be set under (typically `reducerName`) and the value to be set.
 
@@ -860,7 +921,7 @@ const action = createAction(type)
 context.exposeAction(reducerName, action)
 ```
 
-#### `exposeCaseReducer`
+##### `exposeCaseReducer`
 
 Attaches a value to `slice.caseReducers`. Receives the key to be set under (typically `reducerName`) and the value to be set.
 
@@ -868,7 +929,7 @@ Attaches a value to `slice.caseReducers`. Receives the key to be set under (typi
 context.exposeCaseReducer(reducerName, reducer)
 ```
 
-#### `getInitialState`
+##### `getInitialState`
 
 Returns the initial state value for the slice. If a lazy state initializer has been provided, it will be called and a fresh value returned.
 
@@ -881,7 +942,7 @@ context
   .exposeCaseReducer(reducerName, resetReducer)
 ```
 
-### Typescript
+#### Typescript
 
 The Typescript system for custom slice creators uses a "creator registry" system similar to the module system for [RTK Query](/rtk-query/usage/customizing-create-api#creating-your-own-module).
 
@@ -905,7 +966,7 @@ declare module '@reduxjs/toolkit' {
 
 The `ReducerCreatorEntry<Create, Exposes>` utility has two type parameters:
 
-#### `Create`
+##### `Create`
 
 The signature of the `create` method of the creator definition.
 
@@ -1000,13 +1061,13 @@ declare module '@reduxjs/toolkit' {
 
 :::
 
-#### `Exposes`
+##### `Exposes`
 
 The second type parameter for `ReducerCreatorEntry` is optional, but should be used if the creator will handle some reducer definitions itself. It indicates what actions and case reducers will be attached to the slice, and is used to determine the final types of `slice.actions` and `slice.caseReducers`.
 
 It should be an object with some of the following properties:
 
-##### `actions`
+###### `actions`
 
 The actions property will typically be a [mapped type](https://www.typescriptlang.org/docs/handbook/2/mapped-types.html) over the `CaseReducers` type parameter, returning what the creator's `handle` would expose when given that definition.
 
@@ -1048,7 +1109,7 @@ declare module '@reduxjs/toolkit' {
 }
 ```
 
-##### `caseReducers`
+###### `caseReducers`
 
 Similar to `actions`, except for `slice.caseReducers`.
 
@@ -1096,7 +1157,7 @@ declare module '@reduxjs/toolkit' {
 }
 ```
 
-### Further examples
+#### Further examples
 
 This section will cover in depth examples, for potential applications with hopefully applicable lessons to other use cases.
 
@@ -1121,7 +1182,7 @@ export const createAppSlice = buildCreateSlice({
 
 :::
 
-#### Single definitions
+##### Single definitions
 
 Commonly, a creator will return a single reducer definition, to be handled by either itself or another creator.
 
@@ -1279,7 +1340,7 @@ const { showToast } = toastSlice.actions
 toastSlice.caseReducers.showToast.hidden({}, showToast.hidden('id'))
 ```
 
-#### Multiple definitions
+##### Multiple definitions
 
 A creator could also return multiple definitions, which would then be spread into the final definitions object. This is a more composable alternative to the [wrapping `createSlice`](usage/usage-with-typescript#wrapping-createslice) approach, as you could call multiple creators as needed.
 
@@ -1456,7 +1517,7 @@ const postSliceWithHistory = createAppSlice({
 const { undo, redo, reset } = postSliceWithHistory.actions
 ```
 
-#### Other
+##### Other
 
 A creator doesn't have to return any reducer definitions, it could be any sort of utility for defining reducers.
 

From e9321c562b6d8283e40874a294400f762159a05a Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.durrant@marketdojo.com>
Date: Thu, 18 Jan 2024 11:05:59 +0000
Subject: [PATCH 069/178] fix entity slice enhancer test

---
 .../entities/tests/entity_slice_enhancer.test.ts  | 15 +++++++--------
 1 file changed, 7 insertions(+), 8 deletions(-)

diff --git a/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts b/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts
index 9e8e380815..2e54a9260a 100644
--- a/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts
+++ b/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts
@@ -1,17 +1,16 @@
 import { createEntityAdapter, createSlice } from "../..";
-import type { PayloadAction, Slice, SliceCaseReducers, UnknownAction } from "../..";
+import type { PayloadAction, Slice, SliceCaseReducers, UnknownAction, ValidateSliceCaseReducers } from "../..";
 import type { EntityId, EntityState, IdSelector } from "../models";
 import type { BookModel } from "./fixtures/book";
 
 describe('Entity Slice Enhancer', () => {
-  let slice: Slice<EntityState<BookModel, BookModel['id']>>;
+  let slice: ReturnType<typeof entitySliceEnhancer<BookModel, string>>;
 
   beforeEach(() => {
-    const indieSlice = entitySliceEnhancer({
+    slice = entitySliceEnhancer({
       name: 'book',
       selectId: (book: BookModel) => book.id
     })
-    slice = indieSlice
   })
 
   it('exposes oneAdded', () => {
@@ -26,17 +25,17 @@ describe('Entity Slice Enhancer', () => {
   })
 })
 
-interface EntitySliceArgs<T, Id extends EntityId> {
+interface EntitySliceArgs<T, Id extends EntityId, CaseReducers extends SliceCaseReducers<EntityState<T, Id>>> {
   name: string
   selectId: IdSelector<T, Id>
-  modelReducer?: SliceCaseReducers<T>
+  modelReducer?: ValidateSliceCaseReducers<EntityState<T, Id>, CaseReducers>
 }
 
-function entitySliceEnhancer<T, Id extends EntityId>({
+function entitySliceEnhancer<T, Id extends EntityId, CaseReducers extends SliceCaseReducers<EntityState<T, Id>> = {}>({
   name,
   selectId,
   modelReducer
-}: EntitySliceArgs<T, Id>) {
+}: EntitySliceArgs<T, Id, CaseReducers>) {
   const modelAdapter = createEntityAdapter({
     selectId
   });

From d4807a61bffb5250525c254d79e2d527f900b0bf Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.durrant@marketdojo.com>
Date: Thu, 18 Jan 2024 11:07:43 +0000
Subject: [PATCH 070/178] fix createSlice test

---
 packages/toolkit/src/tests/createSlice.test.ts | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index 58dc0a1348..f82b2edc8c 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -709,7 +709,7 @@ describe('createSlice', () => {
       const store = configureStore({
         reducer: slice.reducer,
       })
-      await store.dispatch(slice.actions.thunkReducers('test'))
+      await store.dispatch(slice.actions.thunkReducers())
       expect(store.getState()).toMatchObject([
         [
           'pendingReducer',
@@ -753,7 +753,7 @@ describe('createSlice', () => {
       const store = configureStore({
         reducer: slice.reducer,
       })
-      await store.dispatch(slice.actions.thunkReducers('test'))
+      await store.dispatch(slice.actions.thunkReducers())
       expect(store.getState()).toMatchObject([
         [
           'pendingReducer',
@@ -807,7 +807,7 @@ describe('createSlice', () => {
       const store = configureStore({
         reducer: slice.reducer,
       })
-      await store.dispatch(slice.actions.thunkReducers('test'))
+      await store.dispatch(slice.actions.thunkReducers())
       expect(store.getState()).toMatchObject([
         [
           'rejectedReducer',
@@ -852,8 +852,7 @@ describe('createSlice', () => {
           [],
           slice.actions.thunkReducers.rejected(
             new Error('test'),
-            'fakeRequestId',
-            {}
+            'fakeRequestId'
           )
         )
       ).not.toThrow()

From 209a6e69c857b4b143920d952a358d2c33a5f514 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.durrant@marketdojo.com>
Date: Thu, 18 Jan 2024 11:28:06 +0000
Subject: [PATCH 071/178] fix 4.7 issue

---
 packages/toolkit/src/createSlice.ts | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 4194cd4081..4973251c7f 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -942,8 +942,8 @@ export function buildCreateSlice<
   }
 
   for (const [name, creator] of Object.entries<
-    ReducerCreator<CreatorMap[string]>
-  >(creatorMap)) {
+    ReducerCreator<RegisteredReducerType>
+  >(creatorMap as any)) {
     if (name === 'reducer' || name === 'preparedReducer') {
       throw new Error('Cannot use reserved creator name: ' + name)
     }

From 882ee34170d68f1ef27f0b2397e8870d5e95d958 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.durrant@marketdojo.com>
Date: Thu, 18 Jan 2024 15:01:03 +0000
Subject: [PATCH 072/178] ensure only asyncThunkCreator is passed to asyncThunk
 key

---
 packages/toolkit/src/createSlice.ts | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 4973251c7f..f3ad0bd792 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -954,6 +954,10 @@ export function buildCreateSlice<
       throw new Error(
         `Cannot use reserved creator type: ${String(creator.type)}`
       )
+    } else if (name === 'asyncThunk' && creator !== asyncThunkCreator) {
+      throw new Error(
+        "If provided, `asyncThunk` creator must be `asyncThunkCreator` from '@reduxjs/toolkit'"
+      )
     }
     creators[name] = creator.create
     if ('handle' in creator) {

From e0cabcd586f80700450d288be54bfd3b1c994804 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.durrant@marketdojo.com>
Date: Thu, 18 Jan 2024 15:25:02 +0000
Subject: [PATCH 073/178] rewrite some blurbs

---
 docs/api/createSlice.mdx | 33 ++++++++++++++++++++++++---------
 1 file changed, 24 insertions(+), 9 deletions(-)

diff --git a/docs/api/createSlice.mdx b/docs/api/createSlice.mdx
index dd26655891..67d3d656db 100644
--- a/docs/api/createSlice.mdx
+++ b/docs/api/createSlice.mdx
@@ -528,11 +528,10 @@ It returns an instance of [`createSlice`](#).
 
 ## Slice creators
 
-With the introduction of the [reducer "creator callback" syntax](#the-reducers-creator-callback-notation), the potential was there to allow other, more "custom", `create` methods.
+Redux Toolkit 2.0 introduces the concept of "slice creators", which allow you to define reusable logic for creating slices.
 
-These "creators" would have the capability to:
+These "creators" have the capability to:
 
-- Encapsulate reusable logic
 - Define multiple reducers at the same time
 - Modify slice behaviour by adding case/matcher reducers
 - Expose custom actions (thunks, for example) and case reducers
@@ -566,11 +565,9 @@ const { undo, redo, reset, updateTitle, togglePinned } =
   postSliceWithHistory.actions
 ```
 
-In version TODO, this functionality was added. The below section will cover how to properly define and use these creators, and possible applications.
-
 ### The `reducers` "creator callback" notation
 
-In order to use slice creators, `reducers` should be a callback function. This callback will receive a `create` object, which contains the creators passed to [`buildCreateSlice`](#buildcreateslice), along with a couple of inbuilt creators.
+In order to use slice creators, `reducers` becomes a callback, which receives a `create` object. This `create` object contains a couple of [inbuilt creators](#rtk-creators), along with any creators passed to [`buildCreateSlice`](#buildcreateslice).
 
 ```ts title="Creator callback for reducers"
 import { buildCreateSlice, asyncThunkCreator, nanoid } from '@reduxjs/toolkit'
@@ -635,9 +632,11 @@ export const { addTodo, deleteTodo, fetchTodo } = todosSlice.actions
 
 #### RTK Creators
 
+These creators come built into RTK, and `reducer` and `preparedReducer` are always available on the `create` object passed to the `reducers` callback.
+
 ##### `create.reducer`
 
-A standard slice case reducer.
+A standard slice case reducer. Creates an action creator with the same name as the reducer.
 
 **Parameters**
 
@@ -649,9 +648,15 @@ create.reducer<Todo>((state, action) => {
 })
 ```
 
+:::tip
+
+The [creator definition](#creator-definitions) for `create.reducer` is exported from RTK as `reducerCreator`.
+
+:::
+
 ##### `create.preparedReducer`
 
-A [prepared](#customizing-generated-action-creators) reducer, to customize the action creator.
+A [prepared](#customizing-generated-action-creators) reducer, to customize the action creator. Creates a prepared action creator with the same name as the reducer.
 
 **Parameters**
 
@@ -672,9 +677,15 @@ create.preparedReducer(
 )
 ```
 
+:::tip
+
+The [creator definition](#creator-definitions) for `create.preparedReducer` is exported from RTK as `preparedReducerCreator`.
+
+:::
+
 ##### `create.asyncThunk`
 
-Creates an async thunk instead of an action creator.
+Creates an async thunk and adds any provided case reducers for lifecycle actions.
 
 :::warning Setup
 
@@ -788,6 +799,10 @@ reducers: (create) => {
 
 ### Writing your own creators
 
+In version v2.2.0 (TODO), we introduced a system for including your own creators.
+
+The below documentation will cover how to write your own creators, and how to use them with `createSlice`.
+
 #### Reducer definitions
 
 A reducer definition is an object (or function) with a `_reducerDefinitionType` property indicating which creator should handle it. Other than this property, it is entirely up to the creator what this definition object can look like.

From e2385557d816954d75a98635e0ae24e9157dee10 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.durrant@marketdojo.com>
Date: Thu, 18 Jan 2024 15:46:38 +0000
Subject: [PATCH 074/178] add reserved name warning

---
 docs/api/createSlice.mdx | 20 ++++++++++++++++++--
 1 file changed, 18 insertions(+), 2 deletions(-)

diff --git a/docs/api/createSlice.mdx b/docs/api/createSlice.mdx
index 67d3d656db..10b62f8a31 100644
--- a/docs/api/createSlice.mdx
+++ b/docs/api/createSlice.mdx
@@ -836,9 +836,9 @@ Typically a creator will return a [single reducer definition](#single-definition
 
 #### Creator definitions
 
-A creator definition is an object with a `type` property, a `create` method, and an optional `handle` method.
+A creator definition contains the actual runtime logic for that creator. It's an object with a `type` property, a `create` method, and an optional `handle` method.
 
-It's passed to [`buildCreateSlice`](#buildcreateslice) as part of the `creators` object, and the name the creator is nested under in the `create` object is taken from the `buildCreateSlice` call.
+It's passed to [`buildCreateSlice`](#buildcreateslice) as part of the `creators` object, and the name used when calling `buildCreateSlice` will be the key the creator is nested under in the `create` object.
 
 ```ts no-transpile
 import { buildCreateSlice } from '@reduxjs/toolkit'
@@ -860,6 +860,22 @@ const todoSlice = createSlice({
 })
 ```
 
+:::warning
+
+To avoid collision, names used by [RTK creators](#rtk-creators) are reserved - passing creators under the `reducer` or `preparedReducer` keys is not allowed, and only `asyncThunkCreator` is allowed to be passed under the `asyncThunk` key.
+
+```ts no-transpile
+const createAppSlice = buildCreateSlice({
+  creators: {
+    reducer: aCustomCreator, // not allowed, name is reserved
+    asyncThunk: aCustomCreator, // not allowed, must be asyncThunkCreator
+    asyncThunk: asyncThunkCreator, // allowed
+  },
+})
+```
+
+:::
+
 The `type` property of the definition should be the same constant used for reducer definitions to be handled by this creator. To avoid collision, we recommend using Symbols for this. It's also used for defining/retrieving types - see [Typescript](#typescript).
 
 ```ts no-transpile

From 66afcdebfa7a82299d42aa5d4587513f83a6424a Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.durrant@marketdojo.com>
Date: Mon, 29 Jan 2024 16:54:12 +0000
Subject: [PATCH 075/178] run prettier

---
 packages/toolkit/src/createReducer.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/toolkit/src/createReducer.ts b/packages/toolkit/src/createReducer.ts
index 0e17697932..34a56a22d7 100644
--- a/packages/toolkit/src/createReducer.ts
+++ b/packages/toolkit/src/createReducer.ts
@@ -214,7 +214,7 @@ export function createReducer<S extends NotFunction<any>>(
 }
 
 export function makeGetInitialState<S extends NotFunction<any>>(
-  initialState: S | (() => S)
+  initialState: S | (() => S),
 ) {
   // Ensure the initial state gets frozen either way (if draftable)
   let getInitialState: () => S

From 687aa4685a6ff32d246397b89b152fc62c0085f3 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sat, 3 Feb 2024 15:42:50 +0000
Subject: [PATCH 076/178] Added entity methods creator

---
 .../toolkit/src/entities/create_adapter.ts    |  14 +-
 packages/toolkit/src/entities/index.ts        |   1 +
 .../toolkit/src/entities/slice_creator.ts     | 169 ++++++++++++++++++
 .../tests/entity_slice_enhancer.test-d.ts     |  30 ++++
 .../tests/entity_slice_enhancer.test.ts       |  66 +++++--
 packages/toolkit/src/entities/utils.ts        |   4 +
 6 files changed, 267 insertions(+), 17 deletions(-)
 create mode 100644 packages/toolkit/src/entities/slice_creator.ts
 create mode 100644 packages/toolkit/src/entities/tests/entity_slice_enhancer.test-d.ts

diff --git a/packages/toolkit/src/entities/create_adapter.ts b/packages/toolkit/src/entities/create_adapter.ts
index 726ad82c32..970bfc1581 100644
--- a/packages/toolkit/src/entities/create_adapter.ts
+++ b/packages/toolkit/src/entities/create_adapter.ts
@@ -9,20 +9,20 @@ import { createInitialStateFactory } from './entity_state'
 import { createSelectorsFactory } from './state_selectors'
 import { createSortedStateAdapter } from './sorted_state_adapter'
 import { createUnsortedStateAdapter } from './unsorted_state_adapter'
+import type { WithRequiredProp } from '../tsHelpers'
 
 export interface EntityAdapterOptions<T, Id extends EntityId> {
   selectId?: IdSelector<T, Id>
   sortComparer?: false | Comparer<T>
 }
 
-export function createEntityAdapter<T, Id extends EntityId>(options: {
-  selectId: IdSelector<T, Id>
-  sortComparer?: false | Comparer<T>
-}): EntityAdapter<T, Id>
+export function createEntityAdapter<T, Id extends EntityId>(
+  options: WithRequiredProp<EntityAdapterOptions<T, Id>, 'selectId'>,
+): EntityAdapter<T, Id>
 
-export function createEntityAdapter<T extends { id: EntityId }>(options?: {
-  sortComparer?: false | Comparer<T>
-}): EntityAdapter<T, T['id']>
+export function createEntityAdapter<T extends { id: EntityId }>(
+  options?: Omit<EntityAdapterOptions<T, T['id']>, 'selectId'>,
+): EntityAdapter<T, T['id']>
 
 /**
  *
diff --git a/packages/toolkit/src/entities/index.ts b/packages/toolkit/src/entities/index.ts
index e258527474..1c2e13834f 100644
--- a/packages/toolkit/src/entities/index.ts
+++ b/packages/toolkit/src/entities/index.ts
@@ -6,3 +6,4 @@ export type {
   IdSelector,
   Comparer,
 } from './models'
+export { entityMethodsCreator, entityMethodsCreatorType } from './slice_creator'
diff --git a/packages/toolkit/src/entities/slice_creator.ts b/packages/toolkit/src/entities/slice_creator.ts
new file mode 100644
index 0000000000..1fc6f11c4c
--- /dev/null
+++ b/packages/toolkit/src/entities/slice_creator.ts
@@ -0,0 +1,169 @@
+import type { PayloadAction } from '../createAction'
+import type {
+  SliceCaseReducers,
+  ReducerCreatorEntry,
+  CaseReducerDefinition,
+  ReducerCreator,
+  ReducerCreators,
+} from '../createSlice'
+import type {
+  EntityAdapter,
+  EntityId,
+  EntityState,
+  EntityStateAdapter,
+} from './models'
+import { capitalize } from './utils'
+
+export const entityMethodsCreatorType = Symbol()
+
+type EntityReducers<
+  T,
+  Id extends EntityId,
+  State = EntityState<T, Id>,
+  Single extends string = '',
+  Plural extends string = `${Single}s`,
+> = {
+  [K in keyof EntityStateAdapter<
+    T,
+    Id
+  > as `${K}${Single extends '' ? '' : Capitalize<K extends `${string}One` ? Single : Plural>}`]: EntityStateAdapter<
+    T,
+    Id
+  >[K] extends infer Method
+    ? Method extends (state: any, action: PayloadAction<void>) => any
+      ? CaseReducerDefinition<State, PayloadAction<void>>
+      : Method extends (state: any, action: PayloadAction<infer Payload>) => any
+        ? CaseReducerDefinition<State, PayloadAction<Payload>>
+        : never
+    : never
+}
+
+type entityMethodsCreator<State> =
+  State extends EntityState<infer T, infer Id>
+    ? {
+        <
+          T,
+          Id extends EntityId,
+          Single extends string = '',
+          Plural extends string = `${Single}s`,
+        >(
+          this: ReducerCreators<State>,
+          adapter: EntityAdapter<T, Id>,
+          config: {
+            selectEntityState: (state: State) => EntityState<T, Id>
+            name?: Single
+            pluralName?: Plural
+          },
+        ): EntityReducers<T, Id, State, Single, Plural>
+        <Single extends string = '', Plural extends string = `${Single}s`>(
+          this: ReducerCreators<State>,
+          adapter: EntityAdapter<T, Id>,
+          config?: {
+            name?: Single
+            pluralName?: Plural
+          },
+        ): EntityReducers<T, Id, State, Single, Plural>
+      }
+    : <
+        T,
+        Id extends EntityId,
+        Single extends string = '',
+        Plural extends string = `${Single}s`,
+      >(
+        this: ReducerCreators<State>,
+        adapter: EntityAdapter<T, Id>,
+        config: {
+          selectEntityState: (state: State) => EntityState<T, Id>
+          name?: Single
+          pluralName?: Plural
+        },
+      ) => EntityReducers<T, Id, State, Single, Plural>
+
+declare module '../createSlice' {
+  export interface SliceReducerCreators<
+    State = any,
+    CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
+    Name extends string = string,
+  > {
+    [entityMethodsCreatorType]: ReducerCreatorEntry<entityMethodsCreator<State>>
+  }
+}
+
+export const entityMethodsCreator: ReducerCreator<
+  typeof entityMethodsCreatorType
+> = {
+  type: entityMethodsCreatorType,
+  create(
+    adapter,
+    {
+      selectEntityState = (state) => state,
+      name = '',
+      pluralName = '',
+    }: {
+      selectEntityState?: (state: any) => any
+      name?: '' // required for proper key checking
+      pluralName?: ''
+    } = {},
+  ): EntityReducers<any, EntityId, any> {
+    return {
+      [`addOne${capitalize(name)}` as const]: this.reducer<any>(
+        (state, action) => {
+          adapter.addOne(selectEntityState(state), action.payload)
+        },
+      ),
+      [`addMany${capitalize(pluralName)}` as const]: this.reducer<any>(
+        (state, action) => {
+          adapter.addMany(selectEntityState(state), action.payload)
+        },
+      ),
+      [`setOne${capitalize(name)}` as const]: this.reducer<any>(
+        (state, action) => {
+          adapter.setOne(selectEntityState(state), action.payload)
+        },
+      ),
+      [`setMany${capitalize(pluralName)}` as const]: this.reducer<any>(
+        (state, action) => {
+          adapter.setMany(selectEntityState(state), action.payload)
+        },
+      ),
+      [`setAll${capitalize(pluralName)}` as const]: this.reducer<any>(
+        (state, action) => {
+          adapter.setAll(selectEntityState(state), action.payload)
+        },
+      ),
+      [`removeOne${capitalize(name)}` as const]: this.reducer<any>(
+        (state, action) => {
+          adapter.removeOne(selectEntityState(state), action.payload)
+        },
+      ),
+      [`removeMany${capitalize(pluralName)}` as const]: this.reducer<any>(
+        (state, action) => {
+          adapter.removeMany(selectEntityState(state), action.payload)
+        },
+      ),
+      [`removeAll${capitalize(pluralName)}` as const]: this.reducer((state) => {
+        adapter.removeAll(selectEntityState(state))
+      }),
+      [`upsertOne${capitalize(name)}` as const]: this.reducer<any>(
+        (state, action) => {
+          adapter.upsertOne(selectEntityState(state), action.payload)
+        },
+      ),
+      [`upsertMany${capitalize(pluralName)}` as const]: this.reducer<any>(
+        (state, action) => {
+          adapter.upsertMany(selectEntityState(state), action.payload)
+        },
+      ),
+      [`updateOne${capitalize(name)}` as const]: this.reducer<any>(
+        (state, action) => {
+          adapter.updateOne(selectEntityState(state), action.payload)
+        },
+      ),
+      [`updateMany${capitalize(pluralName)}` as const]: this.reducer<any>(
+        (state, action) => {
+          adapter.updateMany(selectEntityState(state), action.payload)
+        },
+      ),
+    }
+  },
+}
diff --git a/packages/toolkit/src/entities/tests/entity_slice_enhancer.test-d.ts b/packages/toolkit/src/entities/tests/entity_slice_enhancer.test-d.ts
new file mode 100644
index 0000000000..e1f0ed72b8
--- /dev/null
+++ b/packages/toolkit/src/entities/tests/entity_slice_enhancer.test-d.ts
@@ -0,0 +1,30 @@
+import type { PayloadActionCreator } from '../../createAction'
+import { buildCreateSlice } from '../../createSlice'
+import { createEntityAdapter } from '../create_adapter'
+import { entityMethodsCreator } from '../slice_creator'
+import type { BookModel } from './fixtures/book'
+
+describe('Entity Slice Enhancer', () => {
+  const createAppSlice = buildCreateSlice({
+    creators: { entityMethods: entityMethodsCreator },
+  })
+  it('should require selectEntityState if state is not compatible', () => {
+    const bookAdapter = createEntityAdapter<BookModel>()
+    const bookSlice = createAppSlice({
+      name: 'books',
+      initialState: { data: bookAdapter.getInitialState() },
+      reducers: (create) => ({
+        // @ts-expect-error
+        ...create.entityMethods(bookAdapter),
+        // @ts-expect-error
+        ...create.entityMethods(bookAdapter, {}),
+        ...create.entityMethods(bookAdapter, {
+          selectEntityState: (state) => state.data,
+        }),
+      }),
+    })
+    expectTypeOf(bookSlice.actions.addOne).toEqualTypeOf<
+      PayloadActionCreator<BookModel, 'books/addOne'>
+    >()
+  })
+})
diff --git a/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts b/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts
index 7f5458bb4c..a0a71ddfee 100644
--- a/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts
+++ b/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts
@@ -1,4 +1,4 @@
-import { createEntityAdapter, createSlice } from '../..'
+import { buildCreateSlice, createEntityAdapter, createSlice } from '../..'
 import type {
   PayloadAction,
   SliceCaseReducers,
@@ -6,7 +6,8 @@ import type {
   ValidateSliceCaseReducers,
 } from '../..'
 import type { EntityId, EntityState, IdSelector } from '../models'
-import type { BookModel } from './fixtures/book'
+import { entityMethodsCreator } from '../slice_creator'
+import { AClockworkOrange, type BookModel } from './fixtures/book'
 
 describe('Entity Slice Enhancer', () => {
   let slice: ReturnType<typeof entitySliceEnhancer<BookModel, string>>
@@ -19,14 +20,9 @@ describe('Entity Slice Enhancer', () => {
   })
 
   it('exposes oneAdded', () => {
-    const book = {
-      id: '0',
-      title: 'Der Steppenwolf',
-      author: 'Herman Hesse',
-    }
-    const action = slice.actions.oneAdded(book)
-    const oneAdded = slice.reducer(undefined, action as UnknownAction)
-    expect(oneAdded.entities['0']).toBe(book)
+    const action = slice.actions.oneAdded(AClockworkOrange)
+    const oneAdded = slice.reducer(undefined, action)
+    expect(oneAdded.entities['0']).toBe(AClockworkOrange)
   })
 })
 
@@ -60,3 +56,53 @@ function entitySliceEnhancer<
     },
   })
 }
+
+describe('entity slice creator', () => {
+  const createAppSlice = buildCreateSlice({
+    creators: { entityMethods: entityMethodsCreator },
+  })
+
+  const bookAdapter = createEntityAdapter<BookModel>()
+
+  const bookSlice = createAppSlice({
+    name: 'book',
+    initialState: bookAdapter.getInitialState({
+      nested: bookAdapter.getInitialState(),
+    }),
+    reducers: (create) => ({
+      ...create.entityMethods(bookAdapter, {
+        name: 'book',
+      }),
+      ...create.entityMethods(bookAdapter, {
+        selectEntityState: (state) => state.nested,
+        name: 'nestedBook',
+      }),
+    }),
+  })
+
+  it('should generate correct actions', () => {
+    expect(bookSlice.actions.addOneBook).toBeTypeOf('function')
+    expect(bookSlice.actions.addOneNestedBook).toBeTypeOf('function')
+  })
+  it('should handle actions', () => {
+    const withBook = bookSlice.reducer(
+      undefined,
+      bookSlice.actions.addOneBook(AClockworkOrange),
+    )
+    expect(bookAdapter.getSelectors().selectById(withBook, '0')).toBe(
+      AClockworkOrange,
+    )
+
+    const withNestedBook = bookSlice.reducer(
+      withBook,
+      bookSlice.actions.addOneNestedBook(AClockworkOrange),
+    )
+    expect(
+      bookAdapter
+        .getSelectors(
+          (state: ReturnType<typeof bookSlice.reducer>) => state.nested,
+        )
+        .selectById(withNestedBook, '0'),
+    ).toBe(AClockworkOrange)
+  })
+})
diff --git a/packages/toolkit/src/entities/utils.ts b/packages/toolkit/src/entities/utils.ts
index 5e3fb92fc1..0f05762e01 100644
--- a/packages/toolkit/src/entities/utils.ts
+++ b/packages/toolkit/src/entities/utils.ts
@@ -55,3 +55,7 @@ export function splitAddedUpdatedEntities<T, Id extends EntityId>(
   }
   return [added, updated]
 }
+
+export function capitalize<S extends string>(str: S) {
+  return str.replace(str[0], str[0].toUpperCase()) as Capitalize<S>
+}

From b5ca5605837364d0cf0f163951878cfc639ca846 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sat, 3 Feb 2024 15:47:38 +0000
Subject: [PATCH 077/178] test plurals

---
 .../toolkit/src/entities/tests/entity_slice_enhancer.test.ts   | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts b/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts
index a0a71ddfee..c96b19be8d 100644
--- a/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts
+++ b/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts
@@ -76,6 +76,7 @@ describe('entity slice creator', () => {
       ...create.entityMethods(bookAdapter, {
         selectEntityState: (state) => state.nested,
         name: 'nestedBook',
+        pluralName: 'nestedBookies',
       }),
     }),
   })
@@ -83,6 +84,8 @@ describe('entity slice creator', () => {
   it('should generate correct actions', () => {
     expect(bookSlice.actions.addOneBook).toBeTypeOf('function')
     expect(bookSlice.actions.addOneNestedBook).toBeTypeOf('function')
+    expect(bookSlice.actions.addManyBooks).toBeTypeOf('function')
+    expect(bookSlice.actions.addManyNestedBookies).toBeTypeOf('function')
   })
   it('should handle actions', () => {
     const withBook = bookSlice.reducer(

From a8cab7486a1c72e6e249470fa1c5207c67c3a779 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sat, 3 Feb 2024 15:59:18 +0000
Subject: [PATCH 078/178] guard against empty string and "fix" createSlice type

---
 packages/toolkit/src/createSlice.ts                      | 2 +-
 packages/toolkit/src/entities/slice_creator.ts           | 2 +-
 .../src/entities/tests/entity_slice_enhancer.test-d.ts   | 8 +++++---
 .../src/entities/tests/entity_slice_enhancer.test.ts     | 9 ++++++---
 packages/toolkit/src/entities/utils.ts                   | 2 +-
 packages/toolkit/src/index.ts                            | 4 ++++
 6 files changed, 18 insertions(+), 9 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index de8aeb7f2a..a5f33966ef 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -833,7 +833,7 @@ export const reducerCreator: ReducerCreator<ReducerType.reducer> = {
   },
   handle({ type, reducerName }, reducer, context) {
     context
-      .addCase(type, reducer)
+      .addCase(type, reducer as any)
       .exposeCaseReducer(reducerName, reducer)
       .exposeAction(reducerName, createAction(type))
   },
diff --git a/packages/toolkit/src/entities/slice_creator.ts b/packages/toolkit/src/entities/slice_creator.ts
index 1fc6f11c4c..a8b1d0375a 100644
--- a/packages/toolkit/src/entities/slice_creator.ts
+++ b/packages/toolkit/src/entities/slice_creator.ts
@@ -79,7 +79,7 @@ type entityMethodsCreator<State> =
         },
       ) => EntityReducers<T, Id, State, Single, Plural>
 
-declare module '../createSlice' {
+declare module '@reduxjs/toolkit' {
   export interface SliceReducerCreators<
     State = any,
     CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
diff --git a/packages/toolkit/src/entities/tests/entity_slice_enhancer.test-d.ts b/packages/toolkit/src/entities/tests/entity_slice_enhancer.test-d.ts
index e1f0ed72b8..b4cc920ef3 100644
--- a/packages/toolkit/src/entities/tests/entity_slice_enhancer.test-d.ts
+++ b/packages/toolkit/src/entities/tests/entity_slice_enhancer.test-d.ts
@@ -1,7 +1,9 @@
 import type { PayloadActionCreator } from '../../createAction'
-import { buildCreateSlice } from '../../createSlice'
-import { createEntityAdapter } from '../create_adapter'
-import { entityMethodsCreator } from '../slice_creator'
+import {
+  buildCreateSlice,
+  createEntityAdapter,
+  entityMethodsCreator,
+} from '@reduxjs/toolkit'
 import type { BookModel } from './fixtures/book'
 
 describe('Entity Slice Enhancer', () => {
diff --git a/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts b/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts
index c96b19be8d..c0369bb698 100644
--- a/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts
+++ b/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts
@@ -1,12 +1,15 @@
-import { buildCreateSlice, createEntityAdapter, createSlice } from '../..'
+import {
+  buildCreateSlice,
+  createEntityAdapter,
+  createSlice,
+  entityMethodsCreator,
+} from '@reduxjs/toolkit'
 import type {
   PayloadAction,
   SliceCaseReducers,
-  UnknownAction,
   ValidateSliceCaseReducers,
 } from '../..'
 import type { EntityId, EntityState, IdSelector } from '../models'
-import { entityMethodsCreator } from '../slice_creator'
 import { AClockworkOrange, type BookModel } from './fixtures/book'
 
 describe('Entity Slice Enhancer', () => {
diff --git a/packages/toolkit/src/entities/utils.ts b/packages/toolkit/src/entities/utils.ts
index 0f05762e01..e9c64c8121 100644
--- a/packages/toolkit/src/entities/utils.ts
+++ b/packages/toolkit/src/entities/utils.ts
@@ -57,5 +57,5 @@ export function splitAddedUpdatedEntities<T, Id extends EntityId>(
 }
 
 export function capitalize<S extends string>(str: S) {
-  return str.replace(str[0], str[0].toUpperCase()) as Capitalize<S>
+  return str && (str.replace(str[0], str[0].toUpperCase()) as Capitalize<S>)
 }
diff --git a/packages/toolkit/src/index.ts b/packages/toolkit/src/index.ts
index 3bce843e01..9c35b9bd62 100644
--- a/packages/toolkit/src/index.ts
+++ b/packages/toolkit/src/index.ts
@@ -129,6 +129,10 @@ export type {
   IdSelector,
   Comparer,
 } from './entities/models'
+export {
+  entityMethodsCreator,
+  entityMethodsCreatorType,
+} from './entities/slice_creator'
 
 export {
   createAsyncThunk,

From 9bee39c1a4b80e21c3eda7da5e1ee4fa33144fa1 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sat, 3 Feb 2024 16:00:50 +0000
Subject: [PATCH 079/178] fix id in test

---
 .../src/entities/tests/entity_slice_enhancer.test.ts      | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts b/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts
index c0369bb698..2b9dd01552 100644
--- a/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts
+++ b/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts
@@ -95,9 +95,9 @@ describe('entity slice creator', () => {
       undefined,
       bookSlice.actions.addOneBook(AClockworkOrange),
     )
-    expect(bookAdapter.getSelectors().selectById(withBook, '0')).toBe(
-      AClockworkOrange,
-    )
+    expect(
+      bookAdapter.getSelectors().selectById(withBook, AClockworkOrange.id),
+    ).toBe(AClockworkOrange)
 
     const withNestedBook = bookSlice.reducer(
       withBook,
@@ -108,7 +108,7 @@ describe('entity slice creator', () => {
         .getSelectors(
           (state: ReturnType<typeof bookSlice.reducer>) => state.nested,
         )
-        .selectById(withNestedBook, '0'),
+        .selectById(withNestedBook, AClockworkOrange.id),
     ).toBe(AClockworkOrange)
   })
 })

From 86dc55f4397f0954a33333d611e98664713b90ca Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sat, 3 Feb 2024 16:06:14 +0000
Subject: [PATCH 080/178] extract config to variable

---
 .../toolkit/src/entities/slice_creator.ts     | 50 +++++++++++--------
 1 file changed, 29 insertions(+), 21 deletions(-)

diff --git a/packages/toolkit/src/entities/slice_creator.ts b/packages/toolkit/src/entities/slice_creator.ts
index a8b1d0375a..767944dde2 100644
--- a/packages/toolkit/src/entities/slice_creator.ts
+++ b/packages/toolkit/src/entities/slice_creator.ts
@@ -1,3 +1,4 @@
+import { E } from 'esbuild-extra/dist/types-9a0ea18a'
 import type { PayloadAction } from '../createAction'
 import type {
   SliceCaseReducers,
@@ -6,6 +7,7 @@ import type {
   ReducerCreator,
   ReducerCreators,
 } from '../createSlice'
+import type { WithRequiredProp } from '../tsHelpers'
 import type {
   EntityAdapter,
   EntityId,
@@ -38,7 +40,19 @@ type EntityReducers<
     : never
 }
 
-type entityMethodsCreator<State> =
+export interface EntityMethodsCreatorConfig<
+  T,
+  Id extends EntityId,
+  State,
+  Single extends string,
+  Plural extends string,
+> {
+  selectEntityState?: (state: State) => EntityState<T, Id>
+  name?: Single
+  pluralName?: Plural
+}
+
+type EntityMethodsCreator<State> =
   State extends EntityState<infer T, infer Id>
     ? {
         <
@@ -49,19 +63,18 @@ type entityMethodsCreator<State> =
         >(
           this: ReducerCreators<State>,
           adapter: EntityAdapter<T, Id>,
-          config: {
-            selectEntityState: (state: State) => EntityState<T, Id>
-            name?: Single
-            pluralName?: Plural
-          },
+          config: WithRequiredProp<
+            EntityMethodsCreatorConfig<T, Id, State, Single, Plural>,
+            'selectEntityState'
+          >,
         ): EntityReducers<T, Id, State, Single, Plural>
         <Single extends string = '', Plural extends string = `${Single}s`>(
           this: ReducerCreators<State>,
           adapter: EntityAdapter<T, Id>,
-          config?: {
-            name?: Single
-            pluralName?: Plural
-          },
+          config?: Omit<
+            EntityMethodsCreatorConfig<T, Id, State, Single, Plural>,
+            'selectEntityState'
+          >,
         ): EntityReducers<T, Id, State, Single, Plural>
       }
     : <
@@ -72,11 +85,10 @@ type entityMethodsCreator<State> =
       >(
         this: ReducerCreators<State>,
         adapter: EntityAdapter<T, Id>,
-        config: {
-          selectEntityState: (state: State) => EntityState<T, Id>
-          name?: Single
-          pluralName?: Plural
-        },
+        config: WithRequiredProp<
+          EntityMethodsCreatorConfig<T, Id, State, Single, Plural>,
+          'selectEntityState'
+        >,
       ) => EntityReducers<T, Id, State, Single, Plural>
 
 declare module '@reduxjs/toolkit' {
@@ -85,7 +97,7 @@ declare module '@reduxjs/toolkit' {
     CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
     Name extends string = string,
   > {
-    [entityMethodsCreatorType]: ReducerCreatorEntry<entityMethodsCreator<State>>
+    [entityMethodsCreatorType]: ReducerCreatorEntry<EntityMethodsCreator<State>>
   }
 }
 
@@ -99,11 +111,7 @@ export const entityMethodsCreator: ReducerCreator<
       selectEntityState = (state) => state,
       name = '',
       pluralName = '',
-    }: {
-      selectEntityState?: (state: any) => any
-      name?: '' // required for proper key checking
-      pluralName?: ''
-    } = {},
+    }: EntityMethodsCreatorConfig<any, any, any, '', ''> = {},
   ): EntityReducers<any, EntityId, any> {
     return {
       [`addOne${capitalize(name)}` as const]: this.reducer<any>(

From b681ca3f8c86e34983b5d9832770c9197c67b80f Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sat, 3 Feb 2024 19:15:33 +0000
Subject: [PATCH 081/178] fix plurals

---
 packages/toolkit/src/entities/slice_creator.ts              | 6 +++---
 .../src/entities/tests/entity_slice_enhancer.test.ts        | 2 +-
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/packages/toolkit/src/entities/slice_creator.ts b/packages/toolkit/src/entities/slice_creator.ts
index 767944dde2..6836d7934f 100644
--- a/packages/toolkit/src/entities/slice_creator.ts
+++ b/packages/toolkit/src/entities/slice_creator.ts
@@ -32,8 +32,8 @@ type EntityReducers<
     T,
     Id
   >[K] extends infer Method
-    ? Method extends (state: any, action: PayloadAction<void>) => any
-      ? CaseReducerDefinition<State, PayloadAction<void>>
+    ? Method extends (state: any, action: PayloadAction) => any
+      ? CaseReducerDefinition<State, PayloadAction>
       : Method extends (state: any, action: PayloadAction<infer Payload>) => any
         ? CaseReducerDefinition<State, PayloadAction<Payload>>
         : never
@@ -110,7 +110,7 @@ export const entityMethodsCreator: ReducerCreator<
     {
       selectEntityState = (state) => state,
       name = '',
-      pluralName = '',
+      pluralName = name ? `${name}s` : '',
     }: EntityMethodsCreatorConfig<any, any, any, '', ''> = {},
   ): EntityReducers<any, EntityId, any> {
     return {
diff --git a/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts b/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts
index 2b9dd01552..1f17ed942a 100644
--- a/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts
+++ b/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts
@@ -25,7 +25,7 @@ describe('Entity Slice Enhancer', () => {
   it('exposes oneAdded', () => {
     const action = slice.actions.oneAdded(AClockworkOrange)
     const oneAdded = slice.reducer(undefined, action)
-    expect(oneAdded.entities['0']).toBe(AClockworkOrange)
+    expect(oneAdded.entities[AClockworkOrange.id]).toBe(AClockworkOrange)
   })
 })
 

From a14ccd159e314d27b1952532729ef94d1a92657a Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sun, 4 Feb 2024 01:50:57 +0000
Subject: [PATCH 082/178] add entityMethodsCreator to docs

---
 docs/api/createEntityAdapter.mdx |   2 +-
 docs/api/createSlice.mdx         | 174 ++++++++++++++++++++++++++++---
 2 files changed, 161 insertions(+), 15 deletions(-)

diff --git a/docs/api/createEntityAdapter.mdx b/docs/api/createEntityAdapter.mdx
index ffd7d19ede..b605fdb79b 100644
--- a/docs/api/createEntityAdapter.mdx
+++ b/docs/api/createEntityAdapter.mdx
@@ -233,7 +233,7 @@ In other words, they accept a state that looks like `{ids: [], entities: {}}`, a
 
 These CRUD methods may be used in multiple ways:
 
-- They may be passed as case reducers directly to `createReducer` and `createSlice`.
+- They may be passed as case reducers directly to `createReducer` and `createSlice`. (also see the [`create.entityMethods`](./createSlice#createentitymethods-entitymethodscreator) slice creator which can assist with this)
 - They may be used as "mutating" helper methods when called manually, such as a separate hand-written call to `addOne()` inside of an existing case reducer, if the `state` argument is actually an Immer `Draft` value.
 - They may be used as immutable update methods when called manually, if the `state` argument is actually a plain JS object or array.
 
diff --git a/docs/api/createSlice.mdx b/docs/api/createSlice.mdx
index 3c20fc868e..7b9ebc129d 100644
--- a/docs/api/createSlice.mdx
+++ b/docs/api/createSlice.mdx
@@ -632,7 +632,7 @@ export const { addTodo, deleteTodo, fetchTodo } = todosSlice.actions
 
 #### RTK Creators
 
-These creators come built into RTK, and `reducer` and `preparedReducer` are always available on the `create` object passed to the `reducers` callback.
+These creators come built into RTK, and are always available on the `create` object passed to the `reducers` callback.
 
 ##### `create.reducer`
 
@@ -650,7 +650,7 @@ create.reducer<Todo>((state, action) => {
 
 :::tip
 
-The [creator definition](#creator-definitions) for `create.reducer` is exported from RTK as `reducerCreator`.
+The [creator definition](#creator-definitions) for `create.reducer` is exported from RTK as `reducerCreator`, to allow reuse.
 
 :::
 
@@ -679,34 +679,65 @@ create.preparedReducer(
 
 :::tip
 
-The [creator definition](#creator-definitions) for `create.preparedReducer` is exported from RTK as `preparedReducerCreator`.
+The [creator definition](#creator-definitions) for `create.preparedReducer` is exported from RTK as `preparedReducerCreator`, to allow reuse.
 
 :::
 
-##### `create.asyncThunk`
+#### Optional RTK Creators
 
-Creates an async thunk and adds any provided case reducers for lifecycle actions.
-
-:::caution Setup
-
-To avoid pulling `createAsyncThunk` into the bundle size of `createSlice` by default, the `asyncThunk` creator is not included in the default `createSlice` instance.
+These creators are not included in the default `create` object, but can be added by passing them to [`buildCreateSlice`](#buildcreateslice).
 
-This means it will not include an `asyncThunk` property on the `create` object.
-
-Instead, import [`buildCreateSlice`](#buildcreateslice) and `asyncThunkCreator`, and create your own version of `createSlice`:
+The name the creator is available under is based on the key used when calling `buildCreateSlice`. For example, to use `create.asyncThunk`:
 
 ```ts
 import { buildCreateSlice, asyncThunkCreator } from '@reduxjs/toolkit'
 
 export const createAppSlice = buildCreateSlice({
-  creators: { asyncThunk: asyncThunkCreator },
+  creators: {
+    // highlight-next-line
+    asyncThunk: asyncThunkCreator,
+  },
+})
+
+interface Post {
+  id: string
+  text: string
+}
+
+export const postsSlice = createAppSlice({
+  name: 'posts',
+  initialState: [] as Post[],
+  reducers: (create) => ({
+    // highlight-next-line
+    fetchPosts: create.asyncThunk(
+      async () => {
+        const res = await fetch('myApi/posts')
+        return (await res.json()) as Post[]
+      },
+      {
+        fulfilled(state, action) {
+          return action.payload
+        },
+      },
+    ),
+  }),
 })
 ```
 
-Then import this `createAppSlice` as needed instead of the exported version from RTK.
+For clarity these docs will use recommended names.
+
+:::tip
+
+We recommend using `createAppSlice` consistently throughout your app as a replacement for `createSlice`.
+
+This avoids having to consider whether the creators are needed for each slice.
 
 :::
 
+##### `create.asyncThunk` (`asyncThunkCreator`)
+
+Creates an async thunk and adds any provided case reducers for lifecycle actions.
+
 **Parameters**
 
 - **payloadCreator** The thunk [payload creator](./createAsyncThunk#payloadcreator).
@@ -798,6 +829,121 @@ reducers: (create) => {
 
 :::
 
+##### `create.entityMethods` (`entityMethodsCreator`)
+
+Creates a set of reducers for managing a normalized entity state, based on a provided [adapter](./createEntityAdapter).
+
+```ts
+import {
+  createEntityAdapter,
+  buildCreateSlice,
+  entityMethodsCreator,
+} from '@reduxjs/toolkit'
+
+const createAppSlice = buildCreateSlice({
+  creators: { entityMethods: entityMethodsCreator },
+})
+
+interface Post {
+  id: string
+  text: string
+}
+
+const postsAdapter = createEntityAdapter<Post>()
+
+const postsSlice = createAppSlice({
+  name: 'posts',
+  initialState: postsAdapter.getInitialState(),
+  reducers: (create) => ({
+    ...create.entityMethods(postsAdapter),
+  }),
+})
+
+export const { setOne, upsertMany, removeAll, ...etc } = postsSlice.actions
+```
+
+:::caution
+
+Because this creator returns an object of multiple reducer definitions, it should be spread into the final object returned by the `reducers` callback.
+
+:::
+
+:::warning Loss of context
+
+`create.entityMethods` relies on being called with its `this` value set to the `create` object.
+
+This happens automatically when called as `create.entityMethods(adapter)`, but can be accidentally lost if the method is destructured and called separately.
+
+```ts no-transpile
+reducers: ({ entityMethods }) => ({
+  // highlight-next-line
+  // ❌ `this` value has been lost
+  ...entityMethods(postsAdapter),
+})
+```
+
+:::
+
+**Parameters**
+
+- **adapter** The [adapter](./createEntityAdapter) to use.
+- **config** The configuration object. (optional)
+
+The configuration object can contain some of the following options:
+
+**`selectEntityState`**
+
+A selector to retrieve the entity state from the slice state. Defaults to `state => state`, but should be provided if the entity state is nested.
+
+```ts no-transpile
+const postsSlice = createAppSlice({
+  name: 'posts',
+  initialState: { posts: postsAdapter.getInitialState() },
+  reducers: (create) => ({
+    ...create.entityMethods(postsAdapter, {
+      selectEntityState: (state) => state.posts,
+    }),
+  }),
+})
+```
+
+**`name`, `pluralName`**
+
+It's often desirable to modify the reducer names to be specific to the data type being used. These options allow you to do that.
+
+```ts no-transpile
+const postsSlice = createAppSlice({
+  name: 'posts',
+  initialState: postsAdapter.getInitialState(),
+  reducers: (create) => ({
+    ...create.entityMethods(postsAdapter, {
+      name: 'post',
+    }),
+  }),
+})
+
+const { addOnePost, upsertManyPosts, removeAllPosts, ...etc } =
+  postsSlice.actions
+```
+
+`pluralName` defaults to `name + 's'`, but can be provided if this isn't desired.
+
+```ts no-transpile
+const gooseSlice = createAppSlice({
+  name: 'geese',
+  initialState: gooseAdapter.getInitialState(),
+  reducers: (create) => ({
+    ...create.entityMethods(gooseAdapter, {
+      name: 'goose',
+      pluralName: 'geese',
+    }),
+  }),
+})
+
+const { addOneGoose, upsertManyGeese, removeAllGeese, ...etc } =
+  gooseSlice.actions
+```
+
 ### Writing your own creators
 
 In version v2.2.0 (TODO), we introduced a system for including your own creators.

From 2f5704f940b59116f4c42b9035b91296ad00c4e6 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sun, 4 Feb 2024 01:55:25 +0000
Subject: [PATCH 083/178] update errors.json

---
 errors.json | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/errors.json b/errors.json
index 4d0120eec9..284060ff88 100644
--- a/errors.json
+++ b/errors.json
@@ -41,5 +41,6 @@
   "39": "Cannot use reserved creator name: name",
   "40": "Please use reducer creators passed to callback. Each reducer definition must have a `_reducerDefinitionType` property indicating which handler to use.",
   "41": "Cannot use reserved creator type: ",
-  "42": "Unsupported reducer type: "
+  "42": "Unsupported reducer type: ",
+  "43": "If provided, `asyncThunk` creator must be `asyncThunkCreator` from '@reduxjs/toolkit'"
 }
\ No newline at end of file

From 1aece5bca77f7384691c8447bcbe0ef751ce2944 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sun, 4 Feb 2024 02:01:26 +0000
Subject: [PATCH 084/178] ensure consistency between imports and module
 augmentation

---
 packages/toolkit/src/entities/slice_creator.ts | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/packages/toolkit/src/entities/slice_creator.ts b/packages/toolkit/src/entities/slice_creator.ts
index 6836d7934f..1177bb9c17 100644
--- a/packages/toolkit/src/entities/slice_creator.ts
+++ b/packages/toolkit/src/entities/slice_creator.ts
@@ -1,12 +1,10 @@
-import { E } from 'esbuild-extra/dist/types-9a0ea18a'
-import type { PayloadAction } from '../createAction'
 import type {
-  SliceCaseReducers,
-  ReducerCreatorEntry,
-  CaseReducerDefinition,
   ReducerCreator,
-  ReducerCreators,
-} from '../createSlice'
+  ReducerCreatorEntry,
+  SliceCaseReducers,
+} from '@reduxjs/toolkit'
+import type { PayloadAction } from '../createAction'
+import type { CaseReducerDefinition, ReducerCreators } from '../createSlice'
 import type { WithRequiredProp } from '../tsHelpers'
 import type {
   EntityAdapter,
@@ -106,7 +104,7 @@ export const entityMethodsCreator: ReducerCreator<
 > = {
   type: entityMethodsCreatorType,
   create(
-    adapter,
+    adapter: EntityStateAdapter<any, any>,
     {
       selectEntityState = (state) => state,
       name = '',

From f43b01e4e9de14283d96ecd2b6eb3c82f71c4814 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sun, 4 Feb 2024 02:07:19 +0000
Subject: [PATCH 085/178] remove unnecessary annotation

---
 packages/toolkit/src/entities/slice_creator.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/toolkit/src/entities/slice_creator.ts b/packages/toolkit/src/entities/slice_creator.ts
index 1177bb9c17..9dc42fa1ef 100644
--- a/packages/toolkit/src/entities/slice_creator.ts
+++ b/packages/toolkit/src/entities/slice_creator.ts
@@ -104,7 +104,7 @@ export const entityMethodsCreator: ReducerCreator<
 > = {
   type: entityMethodsCreatorType,
   create(
-    adapter: EntityStateAdapter<any, any>,
+    adapter,
     {
       selectEntityState = (state) => state,
       name = '',

From 01d85c9f259ece16aa0a5dc3c7bace8060a1dc4d Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sun, 4 Feb 2024 02:27:23 +0000
Subject: [PATCH 086/178] golf

---
 packages/toolkit/src/entities/slice_creator.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/toolkit/src/entities/slice_creator.ts b/packages/toolkit/src/entities/slice_creator.ts
index 9dc42fa1ef..1ced62bec0 100644
--- a/packages/toolkit/src/entities/slice_creator.ts
+++ b/packages/toolkit/src/entities/slice_creator.ts
@@ -108,7 +108,7 @@ export const entityMethodsCreator: ReducerCreator<
     {
       selectEntityState = (state) => state,
       name = '',
-      pluralName = name ? `${name}s` : '',
+      pluralName = name && `${name}s`,
     }: EntityMethodsCreatorConfig<any, any, any, '', ''> = {},
   ): EntityReducers<any, EntityId, any> {
     return {

From 247c98fca3d70bf4f07bcf8955ccaec0c0461189 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sun, 4 Feb 2024 02:29:02 +0000
Subject: [PATCH 087/178] match title

---
 .../toolkit/src/entities/tests/entity_slice_enhancer.test-d.ts  | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/toolkit/src/entities/tests/entity_slice_enhancer.test-d.ts b/packages/toolkit/src/entities/tests/entity_slice_enhancer.test-d.ts
index b4cc920ef3..df68d7c38e 100644
--- a/packages/toolkit/src/entities/tests/entity_slice_enhancer.test-d.ts
+++ b/packages/toolkit/src/entities/tests/entity_slice_enhancer.test-d.ts
@@ -6,7 +6,7 @@ import {
 } from '@reduxjs/toolkit'
 import type { BookModel } from './fixtures/book'
 
-describe('Entity Slice Enhancer', () => {
+describe('entity slice creator', () => {
   const createAppSlice = buildCreateSlice({
     creators: { entityMethods: entityMethodsCreator },
   })

From 765ec1c49e2cffb13fe1e46451ce4f882b2c5884 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sun, 4 Feb 2024 02:34:28 +0000
Subject: [PATCH 088/178] move collision warning

---
 docs/api/createSlice.mdx | 32 ++++++++++++++++----------------
 1 file changed, 16 insertions(+), 16 deletions(-)

diff --git a/docs/api/createSlice.mdx b/docs/api/createSlice.mdx
index 7b9ebc129d..82ddf21a1e 100644
--- a/docs/api/createSlice.mdx
+++ b/docs/api/createSlice.mdx
@@ -734,6 +734,22 @@ This avoids having to consider whether the creators are needed for each slice.
 
 :::
 
+:::caution
+
+To avoid collision, names used by [RTK creators](#rtk-creators) are reserved - passing creators under the `reducer` or `preparedReducer` keys is not allowed, and only `asyncThunkCreator` is allowed to be passed under the `asyncThunk` key.
+
+```ts no-transpile
+const createAppSlice = buildCreateSlice({
+  creators: {
+    reducer: aCustomCreator, // not allowed, name is reserved
+    asyncThunk: aCustomCreator, // not allowed, must be asyncThunkCreator
+    asyncThunk: asyncThunkCreator, // allowed
+  },
+})
+```
+
+:::
+
 ##### `create.asyncThunk` (`asyncThunkCreator`)
 
 Creates an async thunk and adds any provided case reducers for lifecycle actions.
@@ -1007,22 +1023,6 @@ const todoSlice = createSlice({
 })
 ```
 
-:::warning
-
-To avoid collision, names used by [RTK creators](#rtk-creators) are reserved - passing creators under the `reducer` or `preparedReducer` keys is not allowed, and only `asyncThunkCreator` is allowed to be passed under the `asyncThunk` key.
-
-```ts no-transpile
-const createAppSlice = buildCreateSlice({
-  creators: {
-    reducer: aCustomCreator, // not allowed, name is reserved
-    asyncThunk: aCustomCreator, // not allowed, must be asyncThunkCreator
-    asyncThunk: asyncThunkCreator, // allowed
-  },
-})
-```
-
-:::
-
 The `type` property of the definition should be the same constant used for reducer definitions to be handled by this creator. To avoid collision, we recommend using Symbols for this. It's also used for defining/retrieving types - see [Typescript](#typescript).
 
 ```ts no-transpile

From 8d7ed3f19eed686736dab3b575e2603149cebf4d Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sun, 4 Feb 2024 02:42:00 +0000
Subject: [PATCH 089/178] move ternary

---
 packages/toolkit/src/entities/slice_creator.ts | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/packages/toolkit/src/entities/slice_creator.ts b/packages/toolkit/src/entities/slice_creator.ts
index 1ced62bec0..73d45de881 100644
--- a/packages/toolkit/src/entities/slice_creator.ts
+++ b/packages/toolkit/src/entities/slice_creator.ts
@@ -1,4 +1,5 @@
 import type {
+  CaseReducer,
   ReducerCreator,
   ReducerCreatorEntry,
   SliceCaseReducers,
@@ -21,18 +22,18 @@ type EntityReducers<
   Id extends EntityId,
   State = EntityState<T, Id>,
   Single extends string = '',
-  Plural extends string = `${Single}s`,
+  Plural extends string = Single extends '' ? '' : `${Single}s`,
 > = {
   [K in keyof EntityStateAdapter<
     T,
     Id
-  > as `${K}${Single extends '' ? '' : Capitalize<K extends `${string}One` ? Single : Plural>}`]: EntityStateAdapter<
+  > as `${K}${Capitalize<K extends `${string}One` ? Single : Plural>}`]: EntityStateAdapter<
     T,
     Id
   >[K] extends infer Method
-    ? Method extends (state: any, action: PayloadAction) => any
+    ? Method extends CaseReducer<any, PayloadAction>
       ? CaseReducerDefinition<State, PayloadAction>
-      : Method extends (state: any, action: PayloadAction<infer Payload>) => any
+      : Method extends CaseReducer<any, PayloadAction<infer Payload>>
         ? CaseReducerDefinition<State, PayloadAction<Payload>>
         : never
     : never

From b606bf85e140df27df1aeeb6d5d7dc860e52381a Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sun, 4 Feb 2024 02:44:57 +0000
Subject: [PATCH 090/178] add defaultplural type

---
 packages/toolkit/src/entities/slice_creator.ts | 15 +++++++++++----
 1 file changed, 11 insertions(+), 4 deletions(-)

diff --git a/packages/toolkit/src/entities/slice_creator.ts b/packages/toolkit/src/entities/slice_creator.ts
index 73d45de881..0b1d48f6e1 100644
--- a/packages/toolkit/src/entities/slice_creator.ts
+++ b/packages/toolkit/src/entities/slice_creator.ts
@@ -17,12 +17,16 @@ import { capitalize } from './utils'
 
 export const entityMethodsCreatorType = Symbol()
 
+type DefaultPlural<Single extends string> = Single extends ''
+  ? ''
+  : `${Single}s`
+
 type EntityReducers<
   T,
   Id extends EntityId,
   State = EntityState<T, Id>,
   Single extends string = '',
-  Plural extends string = Single extends '' ? '' : `${Single}s`,
+  Plural extends string = DefaultPlural<Single>,
 > = {
   [K in keyof EntityStateAdapter<
     T,
@@ -58,7 +62,7 @@ type EntityMethodsCreator<State> =
           T,
           Id extends EntityId,
           Single extends string = '',
-          Plural extends string = `${Single}s`,
+          Plural extends string = DefaultPlural<Single>,
         >(
           this: ReducerCreators<State>,
           adapter: EntityAdapter<T, Id>,
@@ -67,7 +71,10 @@ type EntityMethodsCreator<State> =
             'selectEntityState'
           >,
         ): EntityReducers<T, Id, State, Single, Plural>
-        <Single extends string = '', Plural extends string = `${Single}s`>(
+        <
+          Single extends string = '',
+          Plural extends string = DefaultPlural<Single>,
+        >(
           this: ReducerCreators<State>,
           adapter: EntityAdapter<T, Id>,
           config?: Omit<
@@ -80,7 +87,7 @@ type EntityMethodsCreator<State> =
         T,
         Id extends EntityId,
         Single extends string = '',
-        Plural extends string = `${Single}s`,
+        Plural extends string = DefaultPlural<Single>,
       >(
         this: ReducerCreators<State>,
         adapter: EntityAdapter<T, Id>,

From f3612fb0cfef6373e3e398be1288fadc6b228199 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sun, 4 Feb 2024 11:52:56 +0000
Subject: [PATCH 091/178] import reducerCreator directly to avoid reliance on
 `this`

---
 docs/api/createSlice.mdx                      | 16 -------
 .../toolkit/src/entities/slice_creator.ts     | 46 +++++++++----------
 .../tests/entity_slice_enhancer.test.ts       | 25 ++++++++++
 .../toolkit/src/tests/createSlice.test.ts     | 22 +++++++++
 4 files changed, 69 insertions(+), 40 deletions(-)

diff --git a/docs/api/createSlice.mdx b/docs/api/createSlice.mdx
index 82ddf21a1e..2d0eab366a 100644
--- a/docs/api/createSlice.mdx
+++ b/docs/api/createSlice.mdx
@@ -884,22 +884,6 @@ Because this creator returns an object of multiple reducer definitions, it shoul
 
 :::
 
-:::warning Loss of context
-
-`create.entityMethods` relies on being called with its `this` value set to the `create` object.
-
-This happens automatically when called as `create.entityMethods(adapter)`, but can be accidentally lost if the method is destructured and called separately.
-
-```ts no-transpile
-reducers: ({ entityMethods }) => ({
-  // highlight-next-line
-  // ❌ `this` value has been lost
-  ...entityMethods(postsAdapter),
-})
-```
-
-:::
-
 **Parameters**
 
 - **adapter** The [adapter](./createEntityAdapter) to use.
diff --git a/packages/toolkit/src/entities/slice_creator.ts b/packages/toolkit/src/entities/slice_creator.ts
index 0b1d48f6e1..c372077c90 100644
--- a/packages/toolkit/src/entities/slice_creator.ts
+++ b/packages/toolkit/src/entities/slice_creator.ts
@@ -5,7 +5,11 @@ import type {
   SliceCaseReducers,
 } from '@reduxjs/toolkit'
 import type { PayloadAction } from '../createAction'
-import type { CaseReducerDefinition, ReducerCreators } from '../createSlice'
+import {
+  reducerCreator,
+  type CaseReducerDefinition,
+  type ReducerCreators,
+} from '../createSlice'
 import type { WithRequiredProp } from '../tsHelpers'
 import type {
   EntityAdapter,
@@ -64,7 +68,6 @@ type EntityMethodsCreator<State> =
           Single extends string = '',
           Plural extends string = DefaultPlural<Single>,
         >(
-          this: ReducerCreators<State>,
           adapter: EntityAdapter<T, Id>,
           config: WithRequiredProp<
             EntityMethodsCreatorConfig<T, Id, State, Single, Plural>,
@@ -75,7 +78,6 @@ type EntityMethodsCreator<State> =
           Single extends string = '',
           Plural extends string = DefaultPlural<Single>,
         >(
-          this: ReducerCreators<State>,
           adapter: EntityAdapter<T, Id>,
           config?: Omit<
             EntityMethodsCreatorConfig<T, Id, State, Single, Plural>,
@@ -89,7 +91,6 @@ type EntityMethodsCreator<State> =
         Single extends string = '',
         Plural extends string = DefaultPlural<Single>,
       >(
-        this: ReducerCreators<State>,
         adapter: EntityAdapter<T, Id>,
         config: WithRequiredProp<
           EntityMethodsCreatorConfig<T, Id, State, Single, Plural>,
@@ -119,61 +120,58 @@ export const entityMethodsCreator: ReducerCreator<
       pluralName = name && `${name}s`,
     }: EntityMethodsCreatorConfig<any, any, any, '', ''> = {},
   ): EntityReducers<any, EntityId, any> {
+    const reducer = reducerCreator.create
     return {
-      [`addOne${capitalize(name)}` as const]: this.reducer<any>(
-        (state, action) => {
-          adapter.addOne(selectEntityState(state), action.payload)
-        },
-      ),
-      [`addMany${capitalize(pluralName)}` as const]: this.reducer<any>(
+      [`addOne${capitalize(name)}` as const]: reducer<any>((state, action) => {
+        adapter.addOne(selectEntityState(state), action.payload)
+      }),
+      [`addMany${capitalize(pluralName)}` as const]: reducer<any>(
         (state, action) => {
           adapter.addMany(selectEntityState(state), action.payload)
         },
       ),
-      [`setOne${capitalize(name)}` as const]: this.reducer<any>(
-        (state, action) => {
-          adapter.setOne(selectEntityState(state), action.payload)
-        },
-      ),
-      [`setMany${capitalize(pluralName)}` as const]: this.reducer<any>(
+      [`setOne${capitalize(name)}` as const]: reducer<any>((state, action) => {
+        adapter.setOne(selectEntityState(state), action.payload)
+      }),
+      [`setMany${capitalize(pluralName)}` as const]: reducer<any>(
         (state, action) => {
           adapter.setMany(selectEntityState(state), action.payload)
         },
       ),
-      [`setAll${capitalize(pluralName)}` as const]: this.reducer<any>(
+      [`setAll${capitalize(pluralName)}` as const]: reducer<any>(
         (state, action) => {
           adapter.setAll(selectEntityState(state), action.payload)
         },
       ),
-      [`removeOne${capitalize(name)}` as const]: this.reducer<any>(
+      [`removeOne${capitalize(name)}` as const]: reducer<any>(
         (state, action) => {
           adapter.removeOne(selectEntityState(state), action.payload)
         },
       ),
-      [`removeMany${capitalize(pluralName)}` as const]: this.reducer<any>(
+      [`removeMany${capitalize(pluralName)}` as const]: reducer<any>(
         (state, action) => {
           adapter.removeMany(selectEntityState(state), action.payload)
         },
       ),
-      [`removeAll${capitalize(pluralName)}` as const]: this.reducer((state) => {
+      [`removeAll${capitalize(pluralName)}` as const]: reducer((state) => {
         adapter.removeAll(selectEntityState(state))
       }),
-      [`upsertOne${capitalize(name)}` as const]: this.reducer<any>(
+      [`upsertOne${capitalize(name)}` as const]: reducer<any>(
         (state, action) => {
           adapter.upsertOne(selectEntityState(state), action.payload)
         },
       ),
-      [`upsertMany${capitalize(pluralName)}` as const]: this.reducer<any>(
+      [`upsertMany${capitalize(pluralName)}` as const]: reducer<any>(
         (state, action) => {
           adapter.upsertMany(selectEntityState(state), action.payload)
         },
       ),
-      [`updateOne${capitalize(name)}` as const]: this.reducer<any>(
+      [`updateOne${capitalize(name)}` as const]: reducer<any>(
         (state, action) => {
           adapter.updateOne(selectEntityState(state), action.payload)
         },
       ),
-      [`updateMany${capitalize(pluralName)}` as const]: this.reducer<any>(
+      [`updateMany${capitalize(pluralName)}` as const]: reducer<any>(
         (state, action) => {
           adapter.updateMany(selectEntityState(state), action.payload)
         },
diff --git a/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts b/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts
index 1f17ed942a..1df02363f1 100644
--- a/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts
+++ b/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts
@@ -3,6 +3,7 @@ import {
   createEntityAdapter,
   createSlice,
   entityMethodsCreator,
+  preparedReducerCreator,
 } from '@reduxjs/toolkit'
 import type {
   PayloadAction,
@@ -111,4 +112,28 @@ describe('entity slice creator', () => {
         .selectById(withNestedBook, AClockworkOrange.id),
     ).toBe(AClockworkOrange)
   })
+  it('should be able to be called without this context', () => {
+    const bookSlice = createAppSlice({
+      name: 'book',
+      initialState: bookAdapter.getInitialState(),
+      reducers: ({ entityMethods }) => ({
+        ...entityMethods(bookAdapter),
+      }),
+    })
+    expect(bookSlice.actions.addOne).toBeTypeOf('function')
+  })
+  it('can be called with object syntax', () => {
+    const bookSlice = createAppSlice({
+      name: 'book',
+      initialState: bookAdapter.getInitialState(),
+      reducers: {
+        ...entityMethodsCreator.create(bookAdapter, {
+          // state can't be inferred here
+          selectEntityState: (state) => state,
+          name: 'book',
+        }),
+      },
+    })
+    expect(bookSlice.actions.addOneBook).toBeTypeOf('function')
+  })
 })
diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index b1742d8f71..afa82f6b45 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -27,6 +27,7 @@ import {
   createSlice,
   isAnyOf,
   nanoid,
+  preparedReducerCreator,
   reducerCreator,
 } from '@reduxjs/toolkit'
 import {
@@ -927,6 +928,27 @@ describe('createSlice', () => {
       )
     })
   })
+  test('reducer and preparedReducer creators can be invoked for object syntax', () => {
+    const counterSlice = createSlice({
+      name: 'counter',
+      initialState: 0,
+      reducers: {
+        incrementBy: reducerCreator.create<number>(
+          (state, action) => state + action.payload,
+        ),
+        decrementBy: preparedReducerCreator.create(
+          (amount: number) => ({
+            payload: amount,
+          }),
+          (state, action) => state - action.payload,
+        ),
+      },
+    })
+
+    const { incrementBy, decrementBy } = counterSlice.actions
+    expect(counterSlice.reducer(0, incrementBy(1))).toBe(1)
+    expect(counterSlice.reducer(0, decrementBy(3))).toBe(-3)
+  })
   describe('custom slice reducer creators', () => {
     const loaderCreator: ReducerCreator<typeof loaderCreatorType> = {
       type: loaderCreatorType,

From 8714db06a5d26abb203ae553557cbd0a6ce05a5a Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sun, 4 Feb 2024 11:54:45 +0000
Subject: [PATCH 092/178] unused import

---
 packages/toolkit/src/entities/slice_creator.ts | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/packages/toolkit/src/entities/slice_creator.ts b/packages/toolkit/src/entities/slice_creator.ts
index c372077c90..09543191db 100644
--- a/packages/toolkit/src/entities/slice_creator.ts
+++ b/packages/toolkit/src/entities/slice_creator.ts
@@ -5,11 +5,7 @@ import type {
   SliceCaseReducers,
 } from '@reduxjs/toolkit'
 import type { PayloadAction } from '../createAction'
-import {
-  reducerCreator,
-  type CaseReducerDefinition,
-  type ReducerCreators,
-} from '../createSlice'
+import { reducerCreator, type CaseReducerDefinition } from '../createSlice'
 import type { WithRequiredProp } from '../tsHelpers'
 import type {
   EntityAdapter,

From 3bbf1fa8cf7c6ee5da36aeb57df8cbe83cc9bfbe Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sun, 4 Feb 2024 11:56:46 +0000
Subject: [PATCH 093/178] differ internal typing for singular and plural to
 ensure a match

---
 packages/toolkit/src/entities/slice_creator.ts | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/packages/toolkit/src/entities/slice_creator.ts b/packages/toolkit/src/entities/slice_creator.ts
index 09543191db..13ea74b0e3 100644
--- a/packages/toolkit/src/entities/slice_creator.ts
+++ b/packages/toolkit/src/entities/slice_creator.ts
@@ -112,10 +112,10 @@ export const entityMethodsCreator: ReducerCreator<
     adapter,
     {
       selectEntityState = (state) => state,
-      name = '',
-      pluralName = name && `${name}s`,
-    }: EntityMethodsCreatorConfig<any, any, any, '', ''> = {},
-  ): EntityReducers<any, EntityId, any> {
+      name = '' as 's',
+      pluralName = name && (`${name}s` as 'p'),
+    }: EntityMethodsCreatorConfig<any, any, any, 's', 'p'> = {},
+  ): EntityReducers<any, EntityId, any, 's', 'p'> {
     const reducer = reducerCreator.create
     return {
       [`addOne${capitalize(name)}` as const]: reducer<any>((state, action) => {

From 593e420dfdb3704810d6ff34b6dc5186108bb5cc Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sun, 4 Feb 2024 11:59:07 +0000
Subject: [PATCH 094/178] add comment

---
 packages/toolkit/src/entities/slice_creator.ts | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/packages/toolkit/src/entities/slice_creator.ts b/packages/toolkit/src/entities/slice_creator.ts
index 13ea74b0e3..1ef4537d38 100644
--- a/packages/toolkit/src/entities/slice_creator.ts
+++ b/packages/toolkit/src/entities/slice_creator.ts
@@ -112,6 +112,8 @@ export const entityMethodsCreator: ReducerCreator<
     adapter,
     {
       selectEntityState = (state) => state,
+      // template literal computed keys don't keep their type if there's an unresolved generic
+      // so we cast to some intermediate type to at least check we're using the right variables in the right places
       name = '' as 's',
       pluralName = name && (`${name}s` as 'p'),
     }: EntityMethodsCreatorConfig<any, any, any, 's', 'p'> = {},

From 241003f068866bb5eeea9a4bee0d2561c9af9d6d Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sun, 4 Feb 2024 12:03:10 +0000
Subject: [PATCH 095/178] prevent any when calling entityMethodsCreator.create

---
 packages/toolkit/src/entities/slice_creator.ts            | 8 ++++----
 .../src/entities/tests/entity_slice_enhancer.test.ts      | 2 +-
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/packages/toolkit/src/entities/slice_creator.ts b/packages/toolkit/src/entities/slice_creator.ts
index 1ef4537d38..e9485f573e 100644
--- a/packages/toolkit/src/entities/slice_creator.ts
+++ b/packages/toolkit/src/entities/slice_creator.ts
@@ -6,7 +6,7 @@ import type {
 } from '@reduxjs/toolkit'
 import type { PayloadAction } from '../createAction'
 import { reducerCreator, type CaseReducerDefinition } from '../createSlice'
-import type { WithRequiredProp } from '../tsHelpers'
+import type { CastAny, WithRequiredProp } from '../tsHelpers'
 import type {
   EntityAdapter,
   EntityId,
@@ -50,7 +50,7 @@ export interface EntityMethodsCreatorConfig<
   Single extends string,
   Plural extends string,
 > {
-  selectEntityState?: (state: State) => EntityState<T, Id>
+  selectEntityState?: (state: CastAny<State, unknown>) => EntityState<T, Id>
   name?: Single
   pluralName?: Plural
 }
@@ -111,13 +111,13 @@ export const entityMethodsCreator: ReducerCreator<
   create(
     adapter,
     {
-      selectEntityState = (state) => state,
+      selectEntityState = (state) => state as EntityState<any, any>,
       // template literal computed keys don't keep their type if there's an unresolved generic
       // so we cast to some intermediate type to at least check we're using the right variables in the right places
       name = '' as 's',
       pluralName = name && (`${name}s` as 'p'),
     }: EntityMethodsCreatorConfig<any, any, any, 's', 'p'> = {},
-  ): EntityReducers<any, EntityId, any, 's', 'p'> {
+  ): EntityReducers<any, any, any, 's', 'p'> {
     const reducer = reducerCreator.create
     return {
       [`addOne${capitalize(name)}` as const]: reducer<any>((state, action) => {
diff --git a/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts b/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts
index 1df02363f1..a189b21733 100644
--- a/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts
+++ b/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts
@@ -129,7 +129,7 @@ describe('entity slice creator', () => {
       reducers: {
         ...entityMethodsCreator.create(bookAdapter, {
           // state can't be inferred here
-          selectEntityState: (state) => state,
+          selectEntityState: (state) => state as EntityState<BookModel, string>,
           name: 'book',
         }),
       },

From 8adeb42118f4fc9b5b0690f0952cbf4fea093c04 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sun, 4 Feb 2024 12:57:58 +0000
Subject: [PATCH 096/178] improve internal type safety

---
 .../toolkit/src/entities/slice_creator.ts     | 120 +++++++++---------
 1 file changed, 62 insertions(+), 58 deletions(-)

diff --git a/packages/toolkit/src/entities/slice_creator.ts b/packages/toolkit/src/entities/slice_creator.ts
index e9485f573e..86ad9e5e30 100644
--- a/packages/toolkit/src/entities/slice_creator.ts
+++ b/packages/toolkit/src/entities/slice_creator.ts
@@ -6,8 +6,9 @@ import type {
 } from '@reduxjs/toolkit'
 import type { PayloadAction } from '../createAction'
 import { reducerCreator, type CaseReducerDefinition } from '../createSlice'
-import type { CastAny, WithRequiredProp } from '../tsHelpers'
+import type { WithRequiredProp } from '../tsHelpers'
 import type {
+  Update,
   EntityAdapter,
   EntityId,
   EntityState,
@@ -34,13 +35,11 @@ type EntityReducers<
   > as `${K}${Capitalize<K extends `${string}One` ? Single : Plural>}`]: EntityStateAdapter<
     T,
     Id
-  >[K] extends infer Method
-    ? Method extends CaseReducer<any, PayloadAction>
-      ? CaseReducerDefinition<State, PayloadAction>
-      : Method extends CaseReducer<any, PayloadAction<infer Payload>>
-        ? CaseReducerDefinition<State, PayloadAction<Payload>>
-        : never
-    : never
+  >[K] extends (state: any) => any
+    ? CaseReducerDefinition<State, PayloadAction>
+    : EntityStateAdapter<T, Id>[K] extends CaseReducer<any, infer A>
+      ? CaseReducerDefinition<State, A>
+      : never
 }
 
 export interface EntityMethodsCreatorConfig<
@@ -50,7 +49,7 @@ export interface EntityMethodsCreatorConfig<
   Single extends string,
   Plural extends string,
 > {
-  selectEntityState?: (state: CastAny<State, unknown>) => EntityState<T, Id>
+  selectEntityState?: (state: State) => EntityState<T, Id>
   name?: Single
   pluralName?: Plural
 }
@@ -104,49 +103,56 @@ declare module '@reduxjs/toolkit' {
   }
 }
 
-export const entityMethodsCreator: ReducerCreator<
-  typeof entityMethodsCreatorType
-> = {
+export const entityMethodsCreator = {
   type: entityMethodsCreatorType,
-  create(
-    adapter,
+  create<
+    T,
+    Id extends EntityId,
+    State = EntityState<T, Id>,
+    Single extends string = '',
+    Plural extends string = DefaultPlural<Single>,
+  >(
+    adapter: EntityAdapter<T, Id>,
     {
-      selectEntityState = (state) => state as EntityState<any, any>,
-      // template literal computed keys don't keep their type if there's an unresolved generic
-      // so we cast to some intermediate type to at least check we're using the right variables in the right places
-      name = '' as 's',
-      pluralName = name && (`${name}s` as 'p'),
-    }: EntityMethodsCreatorConfig<any, any, any, 's', 'p'> = {},
-  ): EntityReducers<any, any, any, 's', 'p'> {
+      selectEntityState = (state) => state as EntityState<T, Id>,
+      name: nameParam = '' as Single,
+      pluralName: pluralParam = (nameParam && `${nameParam}s`) as Plural,
+    }: EntityMethodsCreatorConfig<T, Id, State, Single, Plural> = {},
+  ): EntityReducers<T, Id, State, Single, Plural> {
+    // template literal computed keys don't keep their type if there's an unresolved generic
+    // so we cast to some intermediate type to at least check we're using the right variables in the right places
+
+    const name = nameParam as 's'
+    const pluralName = pluralParam as 'p'
     const reducer = reducerCreator.create
     return {
-      [`addOne${capitalize(name)}` as const]: reducer<any>((state, action) => {
+      [`addOne${capitalize(name)}` as const]: reducer<T>((state, action) => {
         adapter.addOne(selectEntityState(state), action.payload)
       }),
-      [`addMany${capitalize(pluralName)}` as const]: reducer<any>(
-        (state, action) => {
-          adapter.addMany(selectEntityState(state), action.payload)
-        },
-      ),
-      [`setOne${capitalize(name)}` as const]: reducer<any>((state, action) => {
+      [`addMany${capitalize(pluralName)}` as const]: reducer<
+        readonly T[] | Record<Id, T>
+      >((state, action) => {
+        adapter.addMany(selectEntityState(state), action.payload)
+      }),
+      [`setOne${capitalize(name)}` as const]: reducer<T>((state, action) => {
         adapter.setOne(selectEntityState(state), action.payload)
       }),
-      [`setMany${capitalize(pluralName)}` as const]: reducer<any>(
-        (state, action) => {
-          adapter.setMany(selectEntityState(state), action.payload)
-        },
-      ),
-      [`setAll${capitalize(pluralName)}` as const]: reducer<any>(
-        (state, action) => {
-          adapter.setAll(selectEntityState(state), action.payload)
-        },
-      ),
-      [`removeOne${capitalize(name)}` as const]: reducer<any>(
+      [`setMany${capitalize(pluralName)}` as const]: reducer<
+        readonly T[] | Record<Id, T>
+      >((state, action) => {
+        adapter.setMany(selectEntityState(state), action.payload)
+      }),
+      [`setAll${capitalize(pluralName)}` as const]: reducer<
+        readonly T[] | Record<Id, T>
+      >((state, action) => {
+        adapter.setAll(selectEntityState(state), action.payload)
+      }),
+      [`removeOne${capitalize(name)}` as const]: reducer<Id>(
         (state, action) => {
           adapter.removeOne(selectEntityState(state), action.payload)
         },
       ),
-      [`removeMany${capitalize(pluralName)}` as const]: reducer<any>(
+      [`removeMany${capitalize(pluralName)}` as const]: reducer<readonly Id[]>(
         (state, action) => {
           adapter.removeMany(selectEntityState(state), action.payload)
         },
@@ -154,26 +160,24 @@ export const entityMethodsCreator: ReducerCreator<
       [`removeAll${capitalize(pluralName)}` as const]: reducer((state) => {
         adapter.removeAll(selectEntityState(state))
       }),
-      [`upsertOne${capitalize(name)}` as const]: reducer<any>(
-        (state, action) => {
-          adapter.upsertOne(selectEntityState(state), action.payload)
-        },
-      ),
-      [`upsertMany${capitalize(pluralName)}` as const]: reducer<any>(
-        (state, action) => {
-          adapter.upsertMany(selectEntityState(state), action.payload)
-        },
-      ),
-      [`updateOne${capitalize(name)}` as const]: reducer<any>(
+      [`upsertOne${capitalize(name)}` as const]: reducer<T>((state, action) => {
+        adapter.upsertOne(selectEntityState(state), action.payload)
+      }),
+      [`upsertMany${capitalize(pluralName)}` as const]: reducer<
+        readonly T[] | Record<Id, T>
+      >((state, action) => {
+        adapter.upsertMany(selectEntityState(state), action.payload)
+      }),
+      [`updateOne${capitalize(name)}` as const]: reducer<Update<T, Id>>(
         (state, action) => {
           adapter.updateOne(selectEntityState(state), action.payload)
         },
       ),
-      [`updateMany${capitalize(pluralName)}` as const]: reducer<any>(
-        (state, action) => {
-          adapter.updateMany(selectEntityState(state), action.payload)
-        },
-      ),
-    }
+      [`updateMany${capitalize(pluralName)}` as const]: reducer<
+        readonly Update<T, Id>[]
+      >((state, action) => {
+        adapter.updateMany(selectEntityState(state), action.payload)
+      }),
+    } satisfies EntityReducers<T, Id, State, 's', 'p'> as any
   },
-}
+} satisfies ReducerCreator<typeof entityMethodsCreatorType>

From b7eb6b9da620f6592d92b93b1acd21b3e716ea5a Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sun, 4 Feb 2024 13:57:28 +0000
Subject: [PATCH 097/178] Fix type checking in reducers field by making
 callback into generic

---
 docs/api/createSlice.mdx                      | 29 ++++--
 packages/toolkit/src/createSlice.ts           | 97 ++++++++++++++-----
 .../toolkit/src/entities/slice_creator.ts     |  5 +-
 .../tests/entity_slice_enhancer.test-d.ts     | 41 ++++++++
 .../tests/entity_slice_enhancer.test.ts       | 18 +++-
 packages/toolkit/src/index.ts                 |  1 +
 .../toolkit/src/tests/createSlice.test.ts     |  8 +-
 7 files changed, 156 insertions(+), 43 deletions(-)

diff --git a/docs/api/createSlice.mdx b/docs/api/createSlice.mdx
index 2d0eab366a..826e0c61a2 100644
--- a/docs/api/createSlice.mdx
+++ b/docs/api/createSlice.mdx
@@ -1116,7 +1116,8 @@ const reducerCreatorType = Symbol()
 declare module '@reduxjs/toolkit' {
   export interface SliceReducerCreators<
     State = any,
-    CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
+    CaseReducers extends
+      CreatorCaseReducers<State> = CreatorCaseReducers<State>,
     Name extends string = string,
   > {
     [reducerCreatorType]: ReducerCreatorEntry<
@@ -1144,7 +1145,8 @@ const batchedCreatorType = Symbol()
 declare module '@reduxjs/toolkit' {
   export interface SliceReducerCreators<
     State = any,
-    CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
+    CaseReducers extends
+      CreatorCaseReducers<State> = CreatorCaseReducers<State>,
     Name extends string = string,
   > {
     [batchedCreatorType]: ReducerCreatorEntry<
@@ -1182,7 +1184,8 @@ const loaderCreatorType = Symbol()
 declare module '@reduxjs/toolkit' {
   export interface SliceReducerCreators<
     State = any,
-    CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
+    CaseReducers extends
+      CreatorCaseReducers<State> = CreatorCaseReducers<State>,
     Name extends string = string,
   > {
     [loaderCreatorType]: ReducerCreatorEntry<
@@ -1208,7 +1211,8 @@ const loaderCreatorType = Symbol()
 declare module '@reduxjs/toolkit' {
   export interface SliceReducerCreators<
     State = any,
-    CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
+    CaseReducers extends
+      CreatorCaseReducers<State> = CreatorCaseReducers<State>,
     Name extends string = string,
   > {
     [loaderCreatorType]: ReducerCreatorEntry<
@@ -1243,7 +1247,8 @@ const asyncThunkCreatorType = Symbol()
 declare module '@reduxjs/toolkit' {
   export interface SliceReducerCreators<
     State = any,
-    CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
+    CaseReducers extends
+      CreatorCaseReducers<State> = CreatorCaseReducers<State>,
     Name extends string = string,
   > {
     [asyncThunkCreatorType]: ReducerCreatorEntry<
@@ -1283,7 +1288,8 @@ const preparedReducerType = Symbol()
 declare module '@reduxjs/toolkit' {
   export interface SliceReducerCreators<
     State = any,
-    CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
+    CaseReducers extends
+      CreatorCaseReducers<State> = CreatorCaseReducers<State>,
     Name extends string = string,
   > {
     [preparedReducerType]: ReducerCreatorEntry<
@@ -1387,7 +1393,8 @@ interface ToastThunkCreator<
 declare module '@reduxjs/toolkit' {
   export interface SliceReducerCreators<
     State = any,
-    CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
+    CaseReducers extends
+      CreatorCaseReducers<State> = CreatorCaseReducers<State>,
     Name extends string = string,
   > {
     [toastCreatorType]: ReducerCreatorEntry<
@@ -1516,7 +1523,8 @@ interface PaginationState {
 declare module '@reduxjs/toolkit' {
   export interface SliceReducerCreators<
     State = any,
-    CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
+    CaseReducers extends
+      CreatorCaseReducers<State> = CreatorCaseReducers<State>,
     Name extends string = string,
   > {
     [paginationCreatorType]: ReducerCreatorEntry<
@@ -1586,7 +1594,7 @@ interface HistoryState<T> {
 declare module '@reduxjs/toolkit' {
   export interface SliceReducerCreators<
     State = any,
-    CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
+    CaseReducers extends CreatorCaseReducers<State> = CreatorCaseReducers<State>,
     Name extends string = string
   > {
     [paginationCreatorType]: ReducerCreatorEntry<
@@ -1695,7 +1703,8 @@ interface UndoableMeta {
 declare module '@reduxjs/toolkit' {
   export interface SliceReducerCreators<
     State = any,
-    CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
+    CaseReducers extends
+      CreatorCaseReducers<State> = CreatorCaseReducers<State>,
     Name extends string = string,
   > {
     [undoableCreatorType]: ReducerCreatorEntry<
diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index a5f33966ef..3eebf7e529 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -63,9 +63,13 @@ export type ReducerCreatorEntry<
     : {}
 }
 
+export type CreatorCaseReducers<State> =
+  | Record<string, ReducerDefinition>
+  | SliceCaseReducers<State>
+
 export interface SliceReducerCreators<
   State = any,
-  CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
+  CaseReducers extends CreatorCaseReducers<State> = CreatorCaseReducers<State>,
   Name extends string = string,
 > {
   [ReducerType.reducer]: ReducerCreatorEntry<
@@ -315,7 +319,7 @@ export type ReducerCreator<Type extends RegisteredReducerType> = {
     })
 
 export type ReducerNamesOfType<
-  CaseReducers extends SliceCaseReducers<any>,
+  CaseReducers extends CreatorCaseReducers<any>,
   Type extends RegisteredReducerType,
 > = {
   [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends ReducerDefinition<Type>
@@ -334,7 +338,9 @@ interface InjectIntoConfig<NewReducerPath extends string> extends InjectConfig {
  */
 export interface Slice<
   State = any,
-  CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
+  CaseReducers extends
+    | SliceCaseReducers<State>
+    | Record<string, ReducerDefinition> = SliceCaseReducers<State>,
   Name extends string = string,
   ReducerPath extends string = Name,
   Selectors extends SliceSelectors<State> = SliceSelectors<State>,
@@ -422,7 +428,9 @@ export interface Slice<
  */
 interface InjectedSlice<
   State = any,
-  CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
+  CaseReducers extends
+    | SliceCaseReducers<State>
+    | Record<string, ReducerDefinition> = SliceCaseReducers<State>,
   Name extends string = string,
   ReducerPath extends string = Name,
   Selectors extends SliceSelectors<State> = SliceSelectors<State>,
@@ -463,6 +471,19 @@ interface InjectedSlice<
   selectSlice(state: { [K in ReducerPath]?: State | undefined }): State
 }
 
+type CreatorCallback<
+  State,
+  CreatorMap extends Record<string, RegisteredReducerType>,
+> = (
+  create: ReducerCreators<State, CreatorMap>,
+) => Record<string, ReducerDefinition>
+
+type GetCaseReducers<
+  State,
+  CreatorMap extends Record<string, RegisteredReducerType>,
+  CR extends SliceCaseReducers<State> | CreatorCallback<State, CreatorMap>,
+> = CR extends CreatorCallback<State, CreatorMap> ? ReturnType<CR> : CR
+
 /**
  * Options for `createSlice()`.
  *
@@ -470,7 +491,9 @@ interface InjectedSlice<
  */
 export interface CreateSliceOptions<
   State = any,
-  CR extends SliceCaseReducers<State> = SliceCaseReducers<State>,
+  CR extends
+    | SliceCaseReducers<State>
+    | CreatorCallback<State, CreatorMap> = SliceCaseReducers<State>,
   Name extends string = string,
   ReducerPath extends string = Name,
   Selectors extends SliceSelectors<State> = SliceSelectors<State>,
@@ -496,9 +519,7 @@ export interface CreateSliceOptions<
    * functions. For every action type, a matching action creator will be
    * generated using `createAction()`.
    */
-  reducers:
-    | ValidateSliceCaseReducers<State, CR>
-    | ((create: ReducerCreators<State, CreatorMap>) => CR)
+  reducers: ValidateSliceCaseReducers<State, CR>
 
   /**
    * A callback that receives a *builder* object to define
@@ -687,13 +708,11 @@ interface AsyncThunkCreator<
  *
  * @public
  */
-export type SliceCaseReducers<State> =
-  | Record<string, ReducerDefinition>
-  | Record<
-      string,
-      | CaseReducer<State, PayloadAction<any>>
-      | CaseReducerWithPrepare<State, PayloadAction<any, string, any, any>>
-    >
+export type SliceCaseReducers<State> = Record<
+  string,
+  | CaseReducer<State, PayloadAction<any>>
+  | CaseReducerWithPrepare<State, PayloadAction<any, string, any, any>>
+>
 
 /**
  * The type describing a slice's `selectors` option.
@@ -713,7 +732,9 @@ export type SliceActionType<
  * @public
  */
 export type CaseReducerActions<
-  CaseReducers extends SliceCaseReducers<any>,
+  CaseReducers extends
+    | SliceCaseReducers<any>
+    | Record<string, ReducerDefinition>,
   SliceName extends string,
 > = Id<
   UnionToIntersection<
@@ -755,7 +776,11 @@ type ActionCreatorForCaseReducer<CR, Type extends string> = CR extends (
  *
  * @internal
  */
-type SliceDefinedCaseReducers<CaseReducers extends SliceCaseReducers<any>> = Id<
+type SliceDefinedCaseReducers<
+  CaseReducers extends
+    | SliceCaseReducers<any>
+    | Record<string, ReducerDefinition>,
+> = Id<
   UnionToIntersection<
     SliceReducerCreators<
       any,
@@ -800,7 +825,7 @@ type SliceDefinedSelectors<
  */
 export type ValidateSliceCaseReducers<
   S,
-  ACR extends SliceCaseReducers<S>,
+  ACR extends SliceCaseReducers<S> | CreatorCallback<S, any>,
 > = ACR & {
   [T in keyof ACR]: ACR[T] extends {
     reducer(s: S, action?: infer A): any
@@ -962,7 +987,9 @@ export function buildCreateSlice<
   }
   return function createSlice<
     State,
-    CaseReducers extends SliceCaseReducers<State>,
+    CaseReducers extends
+      | SliceCaseReducers<State>
+      | CreatorCallback<State, CreatorMap>,
     Name extends string,
     Selectors extends SliceSelectors<State>,
     ReducerPath extends string = Name,
@@ -975,7 +1002,13 @@ export function buildCreateSlice<
       Selectors,
       CreatorMap
     >,
-  ): Slice<State, CaseReducers, Name, ReducerPath, Selectors> {
+  ): Slice<
+    State,
+    GetCaseReducers<State, CreatorMap, CaseReducers>,
+    Name,
+    ReducerPath,
+    Selectors
+  > {
     const { name, reducerPath = name as unknown as ReducerPath } = options
     if (!name) {
       throw new Error('`name` is a required option for createSlice')
@@ -1056,7 +1089,7 @@ export function buildCreateSlice<
           reducerName,
           type: getType(name, reducerName),
         }
-        handler(reducerDetails, reducerDefinition, contextMethods)
+        handler(reducerDetails, reducerDefinition as any, contextMethods)
       }
     } else {
       for (const [reducerName, reducerDefinition] of Object.entries(
@@ -1069,13 +1102,13 @@ export function buildCreateSlice<
         if ('reducer' in reducerDefinition) {
           preparedReducerCreator.handle(
             reducerDetails,
-            reducerDefinition,
+            reducerDefinition as any,
             contextMethods,
           )
         } else {
           reducerCreator.handle(
             reducerDetails,
-            reducerDefinition,
+            reducerDefinition as any,
             contextMethods,
           )
         }
@@ -1142,7 +1175,13 @@ export function buildCreateSlice<
       reducerPath: CurrentReducerPath,
       injected = false,
     ): Pick<
-      Slice<State, CaseReducers, Name, CurrentReducerPath, Selectors>,
+      Slice<
+        State,
+        GetCaseReducers<State, CreatorMap, CaseReducers>,
+        Name,
+        CurrentReducerPath,
+        Selectors
+      >,
       'getSelectors' | 'selectors' | 'selectSlice' | 'reducerPath'
     > {
       function selectSlice(state: { [K in CurrentReducerPath]: State }) {
@@ -1192,7 +1231,15 @@ export function buildCreateSlice<
       }
     }
 
-    const slice: Slice<State, CaseReducers, Name, ReducerPath, Selectors> = {
+    const slice: Slice<
+      State,
+      CaseReducers extends CreatorCallback<State, CreatorMap>
+        ? ReturnType<CaseReducers>
+        : CaseReducers,
+      Name,
+      ReducerPath,
+      Selectors
+    > = {
       name,
       reducer,
       actions: context.actionCreators as any,
diff --git a/packages/toolkit/src/entities/slice_creator.ts b/packages/toolkit/src/entities/slice_creator.ts
index 86ad9e5e30..1fa3f3507f 100644
--- a/packages/toolkit/src/entities/slice_creator.ts
+++ b/packages/toolkit/src/entities/slice_creator.ts
@@ -1,8 +1,8 @@
 import type {
   CaseReducer,
+  CreatorCaseReducers,
   ReducerCreator,
   ReducerCreatorEntry,
-  SliceCaseReducers,
 } from '@reduxjs/toolkit'
 import type { PayloadAction } from '../createAction'
 import { reducerCreator, type CaseReducerDefinition } from '../createSlice'
@@ -96,7 +96,8 @@ type EntityMethodsCreator<State> =
 declare module '@reduxjs/toolkit' {
   export interface SliceReducerCreators<
     State = any,
-    CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
+    CaseReducers extends
+      CreatorCaseReducers<State> = CreatorCaseReducers<State>,
     Name extends string = string,
   > {
     [entityMethodsCreatorType]: ReducerCreatorEntry<EntityMethodsCreator<State>>
diff --git a/packages/toolkit/src/entities/tests/entity_slice_enhancer.test-d.ts b/packages/toolkit/src/entities/tests/entity_slice_enhancer.test-d.ts
index df68d7c38e..59684bd207 100644
--- a/packages/toolkit/src/entities/tests/entity_slice_enhancer.test-d.ts
+++ b/packages/toolkit/src/entities/tests/entity_slice_enhancer.test-d.ts
@@ -29,4 +29,45 @@ describe('entity slice creator', () => {
       PayloadActionCreator<BookModel, 'books/addOne'>
     >()
   })
+  it('can be used in object form, and shows error if incompatible', () => {
+    const bookAdapter = createEntityAdapter<BookModel>()
+
+    const initialState = { data: bookAdapter.getInitialState() }
+
+    const bookSlice = createAppSlice({
+      name: 'books',
+      initialState: { data: bookAdapter.getInitialState() },
+      // @ts-expect-error
+      reducers: {
+        ...entityMethodsCreator.create(bookAdapter),
+      },
+    })
+
+    const bookSlice2 = createAppSlice({
+      name: 'books',
+      initialState,
+      reducers: {
+        ...entityMethodsCreator.create(bookAdapter, {
+          // cannot be inferred, needs annotation
+          selectEntityState: (state: typeof initialState) => state.data,
+        }),
+      },
+    })
+
+    expectTypeOf(bookSlice2.actions.addOne).toEqualTypeOf<
+      PayloadActionCreator<BookModel, 'books/addOne'>
+    >()
+
+    const bookSlice3 = createAppSlice({
+      name: 'books',
+      initialState: bookAdapter.getInitialState(),
+      reducers: {
+        ...entityMethodsCreator.create(bookAdapter),
+      },
+    })
+
+    expectTypeOf(bookSlice3.actions.addOne).toEqualTypeOf<
+      PayloadActionCreator<BookModel, 'books/addOne'>
+    >()
+  })
 })
diff --git a/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts b/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts
index a189b21733..d60897e720 100644
--- a/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts
+++ b/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts
@@ -3,7 +3,6 @@ import {
   createEntityAdapter,
   createSlice,
   entityMethodsCreator,
-  preparedReducerCreator,
 } from '@reduxjs/toolkit'
 import type {
   PayloadAction,
@@ -128,12 +127,25 @@ describe('entity slice creator', () => {
       initialState: bookAdapter.getInitialState(),
       reducers: {
         ...entityMethodsCreator.create(bookAdapter, {
-          // state can't be inferred here
-          selectEntityState: (state) => state as EntityState<BookModel, string>,
           name: 'book',
         }),
       },
     })
     expect(bookSlice.actions.addOneBook).toBeTypeOf('function')
+
+    const initialState = { nested: bookAdapter.getInitialState() }
+    const nestedBookSlice = createAppSlice({
+      name: 'book',
+      initialState,
+      reducers: {
+        ...entityMethodsCreator.create(bookAdapter, {
+          // state can't be inferred, so needs to be annotated
+          selectEntityState: (state: typeof initialState) => state.nested,
+          name: 'nestedBook',
+          pluralName: 'nestedBookies',
+        }),
+      },
+    })
+    expect(nestedBookSlice.actions.addOneNestedBook).toBeTypeOf('function')
   })
 })
diff --git a/packages/toolkit/src/index.ts b/packages/toolkit/src/index.ts
index 9c35b9bd62..f875dcab42 100644
--- a/packages/toolkit/src/index.ts
+++ b/packages/toolkit/src/index.ts
@@ -77,6 +77,7 @@ export type {
   Slice,
   CaseReducerActions,
   SliceCaseReducers,
+  CreatorCaseReducers,
   ValidateSliceCaseReducers,
   CaseReducerWithPrepare,
   ReducerCreators,
diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index afa82f6b45..28097bcbb6 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -5,6 +5,7 @@ import type {
   Action,
   CaseReducer,
   CaseReducerDefinition,
+  CreatorCaseReducers,
   PayloadAction,
   PayloadActionCreator,
   ReducerCreator,
@@ -13,7 +14,6 @@ import type {
   ReducerDefinition,
   ReducerNamesOfType,
   SliceActionType,
-  SliceCaseReducers,
   ThunkAction,
   WithSlice,
 } from '@reduxjs/toolkit'
@@ -910,6 +910,7 @@ describe('createSlice', () => {
         createSlice({
           name: 'test',
           initialState: [] as any[],
+          // @ts-expect-error
           reducers: (create) => ({
             prepared: {
               prepare: (p: string, m: number, e: { message: string }) => ({
@@ -917,7 +918,7 @@ describe('createSlice', () => {
                 meta: m,
                 error: e,
               }),
-              reducer: (state, action) => {
+              reducer: (state: any[], action: any) => {
                 state.push(action)
               },
             },
@@ -1237,7 +1238,8 @@ interface UndoableOptions {
 declare module '@reduxjs/toolkit' {
   export interface SliceReducerCreators<
     State = any,
-    CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
+    CaseReducers extends
+      CreatorCaseReducers<State> = CreatorCaseReducers<State>,
     Name extends string = string,
   > {
     [loaderCreatorType]: ReducerCreatorEntry<

From 63777d73d89ff240e95f6e096f9aa499dfa73b85 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sun, 4 Feb 2024 22:53:58 +0000
Subject: [PATCH 098/178] add write permissions

---
 .github/workflows/size.yml | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/size.yml b/.github/workflows/size.yml
index e002e1593e..a6641244ad 100644
--- a/.github/workflows/size.yml
+++ b/.github/workflows/size.yml
@@ -1,5 +1,10 @@
 name: size
-on: [pull_request]
+on:
+  pull_request:
+    branches:
+      - master
+permissions:
+  pull-requests: write
 jobs:
   size:
     runs-on: ubuntu-latest

From 619acff3696874600214d81ef55095b3f137c32a Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sun, 4 Feb 2024 23:34:59 +0000
Subject: [PATCH 099/178] adjust tests

---
 packages/toolkit/src/tests/createEntityAdapter.test-d.ts | 2 +-
 packages/toolkit/src/tests/createSlice.test-d.ts         | 3 ++-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/packages/toolkit/src/tests/createEntityAdapter.test-d.ts b/packages/toolkit/src/tests/createEntityAdapter.test-d.ts
index 0e8a4c54f4..635a1aa0c7 100644
--- a/packages/toolkit/src/tests/createEntityAdapter.test-d.ts
+++ b/packages/toolkit/src/tests/createEntityAdapter.test-d.ts
@@ -114,9 +114,9 @@ describe('type tests', () => {
     createSlice({
       name: 'test',
       initialState: adapter.getInitialState(),
+      // @ts-expect-error
       reducers: {
         addOne: adapter.addOne,
-        // @ts-expect-error
         addOne2: adapter2.addOne,
       },
     })
diff --git a/packages/toolkit/src/tests/createSlice.test-d.ts b/packages/toolkit/src/tests/createSlice.test-d.ts
index 35d8bb21de..d87eb33fc3 100644
--- a/packages/toolkit/src/tests/createSlice.test-d.ts
+++ b/packages/toolkit/src/tests/createSlice.test-d.ts
@@ -12,6 +12,7 @@ import type {
   PayloadActionCreator,
   Reducer,
   ReducerCreators,
+  ReducerDefinition,
   SerializedError,
   SliceCaseReducers,
   ThunkDispatch,
@@ -891,7 +892,7 @@ describe('type tests', () => {
 
     const createGenericSlice = <
       T,
-      Reducers extends SliceCaseReducers<GenericState<T>>,
+      Reducers extends Record<string, ReducerDefinition>,
     >({
       name = '',
       initialState,

From d9020acb88893c47fb8b3ea3d42d4f16315f9a14 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sun, 4 Feb 2024 23:36:19 +0000
Subject: [PATCH 100/178] adjust test more

---
 packages/toolkit/src/tests/createEntityAdapter.test-d.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/toolkit/src/tests/createEntityAdapter.test-d.ts b/packages/toolkit/src/tests/createEntityAdapter.test-d.ts
index 635a1aa0c7..4dc78b9501 100644
--- a/packages/toolkit/src/tests/createEntityAdapter.test-d.ts
+++ b/packages/toolkit/src/tests/createEntityAdapter.test-d.ts
@@ -140,8 +140,8 @@ describe('type tests', () => {
     createSlice({
       name: 'test',
       initialState: { somethingElse: '' },
+      // @ts-expect-error
       reducers: {
-        // @ts-expect-error
         addOne: adapter.addOne,
       },
     })

From 5f8d2f43c95e6d1e019b2573a7f7bae9dd538670 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sun, 4 Feb 2024 23:59:25 +0000
Subject: [PATCH 101/178] avoid satisfies expression

---
 .../toolkit/src/entities/slice_creator.ts     | 157 +++++++++---------
 .../tests/entity_slice_enhancer.test-d.ts     |   7 +-
 .../tests/entity_slice_enhancer.test.ts       |   5 +-
 packages/toolkit/src/index.ts                 |   1 +
 4 files changed, 88 insertions(+), 82 deletions(-)

diff --git a/packages/toolkit/src/entities/slice_creator.ts b/packages/toolkit/src/entities/slice_creator.ts
index 1fa3f3507f..1522c44701 100644
--- a/packages/toolkit/src/entities/slice_creator.ts
+++ b/packages/toolkit/src/entities/slice_creator.ts
@@ -104,81 +104,84 @@ declare module '@reduxjs/toolkit' {
   }
 }
 
-export const entityMethodsCreator = {
-  type: entityMethodsCreatorType,
-  create<
-    T,
-    Id extends EntityId,
-    State = EntityState<T, Id>,
-    Single extends string = '',
-    Plural extends string = DefaultPlural<Single>,
-  >(
-    adapter: EntityAdapter<T, Id>,
-    {
-      selectEntityState = (state) => state as EntityState<T, Id>,
-      name: nameParam = '' as Single,
-      pluralName: pluralParam = (nameParam && `${nameParam}s`) as Plural,
-    }: EntityMethodsCreatorConfig<T, Id, State, Single, Plural> = {},
-  ): EntityReducers<T, Id, State, Single, Plural> {
-    // template literal computed keys don't keep their type if there's an unresolved generic
-    // so we cast to some intermediate type to at least check we're using the right variables in the right places
+export function createEntityMethods<
+  T,
+  Id extends EntityId,
+  State = EntityState<T, Id>,
+  Single extends string = '',
+  Plural extends string = DefaultPlural<Single>,
+>(
+  adapter: EntityAdapter<T, Id>,
+  {
+    selectEntityState = (state) => state as unknown as EntityState<T, Id>,
+    name: nameParam = '' as Single,
+    pluralName: pluralParam = (nameParam && `${nameParam}s`) as Plural,
+  }: EntityMethodsCreatorConfig<T, Id, State, Single, Plural> = {},
+): EntityReducers<T, Id, State, Single, Plural> {
+  // template literal computed keys don't keep their type if there's an unresolved generic
+  // so we cast to some intermediate type to at least check we're using the right variables in the right places
 
-    const name = nameParam as 's'
-    const pluralName = pluralParam as 'p'
-    const reducer = reducerCreator.create
-    return {
-      [`addOne${capitalize(name)}` as const]: reducer<T>((state, action) => {
-        adapter.addOne(selectEntityState(state), action.payload)
-      }),
-      [`addMany${capitalize(pluralName)}` as const]: reducer<
-        readonly T[] | Record<Id, T>
-      >((state, action) => {
-        adapter.addMany(selectEntityState(state), action.payload)
-      }),
-      [`setOne${capitalize(name)}` as const]: reducer<T>((state, action) => {
-        adapter.setOne(selectEntityState(state), action.payload)
-      }),
-      [`setMany${capitalize(pluralName)}` as const]: reducer<
-        readonly T[] | Record<Id, T>
-      >((state, action) => {
-        adapter.setMany(selectEntityState(state), action.payload)
-      }),
-      [`setAll${capitalize(pluralName)}` as const]: reducer<
-        readonly T[] | Record<Id, T>
-      >((state, action) => {
-        adapter.setAll(selectEntityState(state), action.payload)
-      }),
-      [`removeOne${capitalize(name)}` as const]: reducer<Id>(
-        (state, action) => {
-          adapter.removeOne(selectEntityState(state), action.payload)
-        },
-      ),
-      [`removeMany${capitalize(pluralName)}` as const]: reducer<readonly Id[]>(
-        (state, action) => {
-          adapter.removeMany(selectEntityState(state), action.payload)
-        },
-      ),
-      [`removeAll${capitalize(pluralName)}` as const]: reducer((state) => {
-        adapter.removeAll(selectEntityState(state))
-      }),
-      [`upsertOne${capitalize(name)}` as const]: reducer<T>((state, action) => {
-        adapter.upsertOne(selectEntityState(state), action.payload)
-      }),
-      [`upsertMany${capitalize(pluralName)}` as const]: reducer<
-        readonly T[] | Record<Id, T>
-      >((state, action) => {
-        adapter.upsertMany(selectEntityState(state), action.payload)
-      }),
-      [`updateOne${capitalize(name)}` as const]: reducer<Update<T, Id>>(
-        (state, action) => {
-          adapter.updateOne(selectEntityState(state), action.payload)
-        },
-      ),
-      [`updateMany${capitalize(pluralName)}` as const]: reducer<
-        readonly Update<T, Id>[]
-      >((state, action) => {
-        adapter.updateMany(selectEntityState(state), action.payload)
-      }),
-    } satisfies EntityReducers<T, Id, State, 's', 'p'> as any
-  },
-} satisfies ReducerCreator<typeof entityMethodsCreatorType>
+  const name = nameParam as 's'
+  const pluralName = pluralParam as 'p'
+  const reducer = reducerCreator.create
+  const reducers: EntityReducers<T, Id, State, 's', 'p'> = {
+    [`addOne${capitalize(name)}` as const]: reducer<T>((state, action) => {
+      adapter.addOne(selectEntityState(state), action.payload)
+    }),
+    [`addMany${capitalize(pluralName)}` as const]: reducer<
+      readonly T[] | Record<Id, T>
+    >((state, action) => {
+      adapter.addMany(selectEntityState(state), action.payload)
+    }),
+    [`setOne${capitalize(name)}` as const]: reducer<T>((state, action) => {
+      adapter.setOne(selectEntityState(state), action.payload)
+    }),
+    [`setMany${capitalize(pluralName)}` as const]: reducer<
+      readonly T[] | Record<Id, T>
+    >((state, action) => {
+      adapter.setMany(selectEntityState(state), action.payload)
+    }),
+    [`setAll${capitalize(pluralName)}` as const]: reducer<
+      readonly T[] | Record<Id, T>
+    >((state, action) => {
+      adapter.setAll(selectEntityState(state), action.payload)
+    }),
+    [`removeOne${capitalize(name)}` as const]: reducer<Id>((state, action) => {
+      adapter.removeOne(selectEntityState(state), action.payload)
+    }),
+    [`removeMany${capitalize(pluralName)}` as const]: reducer<readonly Id[]>(
+      (state, action) => {
+        adapter.removeMany(selectEntityState(state), action.payload)
+      },
+    ),
+    [`removeAll${capitalize(pluralName)}` as const]: reducer((state) => {
+      adapter.removeAll(selectEntityState(state))
+    }),
+    [`upsertOne${capitalize(name)}` as const]: reducer<T>((state, action) => {
+      adapter.upsertOne(selectEntityState(state), action.payload)
+    }),
+    [`upsertMany${capitalize(pluralName)}` as const]: reducer<
+      readonly T[] | Record<Id, T>
+    >((state, action) => {
+      adapter.upsertMany(selectEntityState(state), action.payload)
+    }),
+    [`updateOne${capitalize(name)}` as const]: reducer<Update<T, Id>>(
+      (state, action) => {
+        adapter.updateOne(selectEntityState(state), action.payload)
+      },
+    ),
+    [`updateMany${capitalize(pluralName)}` as const]: reducer<
+      readonly Update<T, Id>[]
+    >((state, action) => {
+      adapter.updateMany(selectEntityState(state), action.payload)
+    }),
+  }
+  return reducers as any
+}
+
+export const entityMethodsCreator: ReducerCreator<
+  typeof entityMethodsCreatorType
+> = {
+  type: entityMethodsCreatorType,
+  create: createEntityMethods,
+}
diff --git a/packages/toolkit/src/entities/tests/entity_slice_enhancer.test-d.ts b/packages/toolkit/src/entities/tests/entity_slice_enhancer.test-d.ts
index 59684bd207..29eca94da9 100644
--- a/packages/toolkit/src/entities/tests/entity_slice_enhancer.test-d.ts
+++ b/packages/toolkit/src/entities/tests/entity_slice_enhancer.test-d.ts
@@ -3,6 +3,7 @@ import {
   buildCreateSlice,
   createEntityAdapter,
   entityMethodsCreator,
+  createEntityMethods,
 } from '@reduxjs/toolkit'
 import type { BookModel } from './fixtures/book'
 
@@ -29,7 +30,7 @@ describe('entity slice creator', () => {
       PayloadActionCreator<BookModel, 'books/addOne'>
     >()
   })
-  it('can be used in object form, and shows error if incompatible', () => {
+  it('exports createEntityMethods which can be used in object form', () => {
     const bookAdapter = createEntityAdapter<BookModel>()
 
     const initialState = { data: bookAdapter.getInitialState() }
@@ -39,7 +40,7 @@ describe('entity slice creator', () => {
       initialState: { data: bookAdapter.getInitialState() },
       // @ts-expect-error
       reducers: {
-        ...entityMethodsCreator.create(bookAdapter),
+        ...createEntityMethods(bookAdapter),
       },
     })
 
@@ -62,7 +63,7 @@ describe('entity slice creator', () => {
       name: 'books',
       initialState: bookAdapter.getInitialState(),
       reducers: {
-        ...entityMethodsCreator.create(bookAdapter),
+        ...createEntityMethods(bookAdapter),
       },
     })
 
diff --git a/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts b/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts
index d60897e720..af5cab2638 100644
--- a/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts
+++ b/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts
@@ -3,6 +3,7 @@ import {
   createEntityAdapter,
   createSlice,
   entityMethodsCreator,
+  createEntityMethods,
 } from '@reduxjs/toolkit'
 import type {
   PayloadAction,
@@ -126,7 +127,7 @@ describe('entity slice creator', () => {
       name: 'book',
       initialState: bookAdapter.getInitialState(),
       reducers: {
-        ...entityMethodsCreator.create(bookAdapter, {
+        ...createEntityMethods(bookAdapter, {
           name: 'book',
         }),
       },
@@ -138,7 +139,7 @@ describe('entity slice creator', () => {
       name: 'book',
       initialState,
       reducers: {
-        ...entityMethodsCreator.create(bookAdapter, {
+        ...createEntityMethods(bookAdapter, {
           // state can't be inferred, so needs to be annotated
           selectEntityState: (state: typeof initialState) => state.nested,
           name: 'nestedBook',
diff --git a/packages/toolkit/src/index.ts b/packages/toolkit/src/index.ts
index f875dcab42..05fd217831 100644
--- a/packages/toolkit/src/index.ts
+++ b/packages/toolkit/src/index.ts
@@ -131,6 +131,7 @@ export type {
   Comparer,
 } from './entities/models'
 export {
+  createEntityMethods,
   entityMethodsCreator,
   entityMethodsCreatorType,
 } from './entities/slice_creator'

From e1f3f8970ce9078a3b9a31d6a6ca2104ee915010 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Mon, 5 Feb 2024 00:24:55 +0000
Subject: [PATCH 102/178] tweak createSlice logic

---
 packages/toolkit/src/createSlice.ts | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 3eebf7e529..f0fede9463 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -928,6 +928,14 @@ export const asyncThunkCreator: ReducerCreator<ReducerType.asyncThunk> = {
 
 function noop() {}
 
+const isCreatorCallback = <
+  State,
+  CreatorMap extends Record<string, RegisteredReducerType>,
+>(
+  reducers: any,
+): reducers is CreatorCallback<State, CreatorMap> =>
+  typeof reducers === 'function'
+
 interface BuildCreateSliceConfig<
   CreatorMap extends Record<string, RegisteredReducerType>,
 > {
@@ -1072,7 +1080,7 @@ export function buildCreateSlice<
       getInitialState,
     }
 
-    if (typeof options.reducers === 'function') {
+    if (isCreatorCallback(options.reducers)) {
       const reducers = options.reducers(creators as any)
       for (const [reducerName, reducerDefinition] of Object.entries(reducers)) {
         const { _reducerDefinitionType: type } = reducerDefinition
@@ -1093,7 +1101,7 @@ export function buildCreateSlice<
       }
     } else {
       for (const [reducerName, reducerDefinition] of Object.entries(
-        options.reducers,
+        options.reducers as SliceCaseReducers<State>,
       )) {
         const reducerDetails: ReducerDetails = {
           reducerName,

From f559617fc868c87876dfa29752a5284e5a59cd78 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Mon, 5 Feb 2024 00:31:52 +0000
Subject: [PATCH 103/178] try adding a pure

---
 packages/toolkit/src/entities/slice_creator.ts | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/packages/toolkit/src/entities/slice_creator.ts b/packages/toolkit/src/entities/slice_creator.ts
index 1522c44701..472e142502 100644
--- a/packages/toolkit/src/entities/slice_creator.ts
+++ b/packages/toolkit/src/entities/slice_creator.ts
@@ -104,7 +104,7 @@ declare module '@reduxjs/toolkit' {
   }
 }
 
-export function createEntityMethods<
+/*#__PURE__*/ export function createEntityMethods<
   T,
   Id extends EntityId,
   State = EntityState<T, Id>,
@@ -181,7 +181,7 @@ export function createEntityMethods<
 
 export const entityMethodsCreator: ReducerCreator<
   typeof entityMethodsCreatorType
-> = {
+> = /*#__PURE__*/ {
   type: entityMethodsCreatorType,
   create: createEntityMethods,
 }

From 162e4828bd7d0831a975c4bd8b5d2ef6c4978600 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Mon, 5 Feb 2024 00:36:15 +0000
Subject: [PATCH 104/178] pure more of the things

---
 packages/toolkit/src/createSlice.ts           | 136 +++++++++---------
 .../toolkit/src/entities/slice_creator.ts     |   2 +-
 2 files changed, 70 insertions(+), 68 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index f0fede9463..3076d9f9ed 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -840,32 +840,33 @@ function getType(slice: string, actionKey: string): string {
   return `${slice}/${actionKey}`
 }
 
-export const reducerCreator: ReducerCreator<ReducerType.reducer> = {
-  type: ReducerType.reducer,
-  create(caseReducer: CaseReducer<any, any>) {
-    return Object.assign(
-      {
-        // hack so the wrapping function has the same name as the original
-        // we need to create a wrapper so the `reducerDefinitionType` is not assigned to the original
-        [caseReducer.name](...args: Parameters<typeof caseReducer>) {
-          return caseReducer(...args)
-        },
-      }[caseReducer.name],
-      {
-        _reducerDefinitionType: ReducerType.reducer,
-      } as const,
-    )
-  },
-  handle({ type, reducerName }, reducer, context) {
-    context
-      .addCase(type, reducer as any)
-      .exposeCaseReducer(reducerName, reducer)
-      .exposeAction(reducerName, createAction(type))
-  },
-}
+export const reducerCreator: ReducerCreator<ReducerType.reducer> =
+  /*#__PURE__*/ {
+    type: ReducerType.reducer,
+    create(caseReducer: CaseReducer<any, any>) {
+      return Object.assign(
+        {
+          // hack so the wrapping function has the same name as the original
+          // we need to create a wrapper so the `reducerDefinitionType` is not assigned to the original
+          [caseReducer.name](...args: Parameters<typeof caseReducer>) {
+            return caseReducer(...args)
+          },
+        }[caseReducer.name],
+        {
+          _reducerDefinitionType: ReducerType.reducer,
+        } as const,
+      )
+    },
+    handle({ type, reducerName }, reducer, context) {
+      context
+        .addCase(type, reducer as any)
+        .exposeCaseReducer(reducerName, reducer)
+        .exposeAction(reducerName, createAction(type))
+    },
+  }
 
 export const preparedReducerCreator: ReducerCreator<ReducerType.reducerWithPrepare> =
-  {
+  /*#__PURE__*/ {
     type: ReducerType.reducerWithPrepare,
     create(prepare, reducer) {
       return {
@@ -882,53 +883,54 @@ export const preparedReducerCreator: ReducerCreator<ReducerType.reducerWithPrepa
     },
   }
 
-export const asyncThunkCreator: ReducerCreator<ReducerType.asyncThunk> = {
-  type: ReducerType.asyncThunk,
-  create: /* @__PURE__ */ (() => {
-    function asyncThunk(
-      payloadCreator: AsyncThunkPayloadCreator<any, any>,
-      config: AsyncThunkSliceReducerConfig<any, any>,
-    ): AsyncThunkSliceReducerDefinition<any, any> {
-      return {
-        _reducerDefinitionType: ReducerType.asyncThunk,
-        payloadCreator,
-        ...config,
+export const asyncThunkCreator: ReducerCreator<ReducerType.asyncThunk> =
+  /*#__PURE__*/ {
+    type: ReducerType.asyncThunk,
+    create: /* @__PURE__ */ (() => {
+      function asyncThunk(
+        payloadCreator: AsyncThunkPayloadCreator<any, any>,
+        config: AsyncThunkSliceReducerConfig<any, any>,
+      ): AsyncThunkSliceReducerDefinition<any, any> {
+        return {
+          _reducerDefinitionType: ReducerType.asyncThunk,
+          payloadCreator,
+          ...config,
+        }
+      }
+      asyncThunk.withTypes = () => asyncThunk
+      return asyncThunk as AsyncThunkCreator<any>
+    })(),
+    handle({ type, reducerName }, definition, context) {
+      const { payloadCreator, fulfilled, pending, rejected, settled, options } =
+        definition
+      const thunk = createAsyncThunk(type, payloadCreator, options as any)
+      context.exposeAction(reducerName, thunk)
+
+      if (fulfilled) {
+        context.addCase(thunk.fulfilled, fulfilled)
+      }
+      if (pending) {
+        context.addCase(thunk.pending, pending)
+      }
+      if (rejected) {
+        context.addCase(thunk.rejected, rejected)
+      }
+      if (settled) {
+        context.addMatcher(thunk.settled, settled)
       }
-    }
-    asyncThunk.withTypes = () => asyncThunk
-    return asyncThunk as AsyncThunkCreator<any>
-  })(),
-  handle({ type, reducerName }, definition, context) {
-    const { payloadCreator, fulfilled, pending, rejected, settled, options } =
-      definition
-    const thunk = createAsyncThunk(type, payloadCreator, options as any)
-    context.exposeAction(reducerName, thunk)
-
-    if (fulfilled) {
-      context.addCase(thunk.fulfilled, fulfilled)
-    }
-    if (pending) {
-      context.addCase(thunk.pending, pending)
-    }
-    if (rejected) {
-      context.addCase(thunk.rejected, rejected)
-    }
-    if (settled) {
-      context.addMatcher(thunk.settled, settled)
-    }
 
-    context.exposeCaseReducer(reducerName, {
-      fulfilled: fulfilled || noop,
-      pending: pending || noop,
-      rejected: rejected || noop,
-      settled: settled || noop,
-    })
-  },
-}
+      context.exposeCaseReducer(reducerName, {
+        fulfilled: fulfilled || noop,
+        pending: pending || noop,
+        rejected: rejected || noop,
+        settled: settled || noop,
+      })
+    },
+  }
 
 function noop() {}
 
-const isCreatorCallback = <
+const isCreatorCallback = /*#__PURE__*/ <
   State,
   CreatorMap extends Record<string, RegisteredReducerType>,
 >(
@@ -1298,4 +1300,4 @@ function wrapSelector<State, NewState, S extends Selector<State>>(
  *
  * @public
  */
-export const createSlice = buildCreateSlice()
+export const createSlice = /*#__PURE__*/ buildCreateSlice()
diff --git a/packages/toolkit/src/entities/slice_creator.ts b/packages/toolkit/src/entities/slice_creator.ts
index 472e142502..1d1de5f3b9 100644
--- a/packages/toolkit/src/entities/slice_creator.ts
+++ b/packages/toolkit/src/entities/slice_creator.ts
@@ -16,7 +16,7 @@ import type {
 } from './models'
 import { capitalize } from './utils'
 
-export const entityMethodsCreatorType = Symbol()
+export const entityMethodsCreatorType = /*#__PURE__*/ Symbol()
 
 type DefaultPlural<Single extends string> = Single extends ''
   ? ''

From 611bd97cc48caa2ab6aaea00647f17ba02f1c4c4 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.durrant@marketdojo.com>
Date: Mon, 5 Feb 2024 15:05:12 +0000
Subject: [PATCH 105/178] remove unnecessary pures

---
 packages/toolkit/src/createSlice.ts           | 136 +++++++++---------
 .../toolkit/src/entities/slice_creator.ts     |   6 +-
 2 files changed, 70 insertions(+), 72 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 3076d9f9ed..21816d4753 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -840,33 +840,32 @@ function getType(slice: string, actionKey: string): string {
   return `${slice}/${actionKey}`
 }
 
-export const reducerCreator: ReducerCreator<ReducerType.reducer> =
-  /*#__PURE__*/ {
-    type: ReducerType.reducer,
-    create(caseReducer: CaseReducer<any, any>) {
-      return Object.assign(
-        {
-          // hack so the wrapping function has the same name as the original
-          // we need to create a wrapper so the `reducerDefinitionType` is not assigned to the original
-          [caseReducer.name](...args: Parameters<typeof caseReducer>) {
-            return caseReducer(...args)
-          },
-        }[caseReducer.name],
-        {
-          _reducerDefinitionType: ReducerType.reducer,
-        } as const,
-      )
-    },
-    handle({ type, reducerName }, reducer, context) {
-      context
-        .addCase(type, reducer as any)
-        .exposeCaseReducer(reducerName, reducer)
-        .exposeAction(reducerName, createAction(type))
-    },
-  }
+export const reducerCreator: ReducerCreator<ReducerType.reducer> = {
+  type: ReducerType.reducer,
+  create(caseReducer: CaseReducer<any, any>) {
+    return Object.assign(
+      {
+        // hack so the wrapping function has the same name as the original
+        // we need to create a wrapper so the `reducerDefinitionType` is not assigned to the original
+        [caseReducer.name](...args: Parameters<typeof caseReducer>) {
+          return caseReducer(...args)
+        },
+      }[caseReducer.name],
+      {
+        _reducerDefinitionType: ReducerType.reducer,
+      } as const,
+    )
+  },
+  handle({ type, reducerName }, reducer, context) {
+    context
+      .addCase(type, reducer as any)
+      .exposeCaseReducer(reducerName, reducer)
+      .exposeAction(reducerName, createAction(type))
+  },
+}
 
 export const preparedReducerCreator: ReducerCreator<ReducerType.reducerWithPrepare> =
-  /*#__PURE__*/ {
+  /*@__PURE__*/ {
     type: ReducerType.reducerWithPrepare,
     create(prepare, reducer) {
       return {
@@ -883,54 +882,53 @@ export const preparedReducerCreator: ReducerCreator<ReducerType.reducerWithPrepa
     },
   }
 
-export const asyncThunkCreator: ReducerCreator<ReducerType.asyncThunk> =
-  /*#__PURE__*/ {
-    type: ReducerType.asyncThunk,
-    create: /* @__PURE__ */ (() => {
-      function asyncThunk(
-        payloadCreator: AsyncThunkPayloadCreator<any, any>,
-        config: AsyncThunkSliceReducerConfig<any, any>,
-      ): AsyncThunkSliceReducerDefinition<any, any> {
-        return {
-          _reducerDefinitionType: ReducerType.asyncThunk,
-          payloadCreator,
-          ...config,
-        }
-      }
-      asyncThunk.withTypes = () => asyncThunk
-      return asyncThunk as AsyncThunkCreator<any>
-    })(),
-    handle({ type, reducerName }, definition, context) {
-      const { payloadCreator, fulfilled, pending, rejected, settled, options } =
-        definition
-      const thunk = createAsyncThunk(type, payloadCreator, options as any)
-      context.exposeAction(reducerName, thunk)
-
-      if (fulfilled) {
-        context.addCase(thunk.fulfilled, fulfilled)
-      }
-      if (pending) {
-        context.addCase(thunk.pending, pending)
-      }
-      if (rejected) {
-        context.addCase(thunk.rejected, rejected)
-      }
-      if (settled) {
-        context.addMatcher(thunk.settled, settled)
+export const asyncThunkCreator: ReducerCreator<ReducerType.asyncThunk> = {
+  type: ReducerType.asyncThunk,
+  create: /* @__PURE__ */ (() => {
+    function asyncThunk(
+      payloadCreator: AsyncThunkPayloadCreator<any, any>,
+      config: AsyncThunkSliceReducerConfig<any, any>,
+    ): AsyncThunkSliceReducerDefinition<any, any> {
+      return {
+        _reducerDefinitionType: ReducerType.asyncThunk,
+        payloadCreator,
+        ...config,
       }
+    }
+    asyncThunk.withTypes = () => asyncThunk
+    return asyncThunk as AsyncThunkCreator<any>
+  })(),
+  handle({ type, reducerName }, definition, context) {
+    const { payloadCreator, fulfilled, pending, rejected, settled, options } =
+      definition
+    const thunk = createAsyncThunk(type, payloadCreator, options as any)
+    context.exposeAction(reducerName, thunk)
+
+    if (fulfilled) {
+      context.addCase(thunk.fulfilled, fulfilled)
+    }
+    if (pending) {
+      context.addCase(thunk.pending, pending)
+    }
+    if (rejected) {
+      context.addCase(thunk.rejected, rejected)
+    }
+    if (settled) {
+      context.addMatcher(thunk.settled, settled)
+    }
 
-      context.exposeCaseReducer(reducerName, {
-        fulfilled: fulfilled || noop,
-        pending: pending || noop,
-        rejected: rejected || noop,
-        settled: settled || noop,
-      })
-    },
-  }
+    context.exposeCaseReducer(reducerName, {
+      fulfilled: fulfilled || noop,
+      pending: pending || noop,
+      rejected: rejected || noop,
+      settled: settled || noop,
+    })
+  },
+}
 
 function noop() {}
 
-const isCreatorCallback = /*#__PURE__*/ <
+const isCreatorCallback = <
   State,
   CreatorMap extends Record<string, RegisteredReducerType>,
 >(
@@ -1300,4 +1298,4 @@ function wrapSelector<State, NewState, S extends Selector<State>>(
  *
  * @public
  */
-export const createSlice = /*#__PURE__*/ buildCreateSlice()
+export const createSlice = /*@__PURE__*/ buildCreateSlice()
diff --git a/packages/toolkit/src/entities/slice_creator.ts b/packages/toolkit/src/entities/slice_creator.ts
index 1d1de5f3b9..addcd49284 100644
--- a/packages/toolkit/src/entities/slice_creator.ts
+++ b/packages/toolkit/src/entities/slice_creator.ts
@@ -16,7 +16,7 @@ import type {
 } from './models'
 import { capitalize } from './utils'
 
-export const entityMethodsCreatorType = /*#__PURE__*/ Symbol()
+export const entityMethodsCreatorType = /*@__PURE__*/ Symbol()
 
 type DefaultPlural<Single extends string> = Single extends ''
   ? ''
@@ -104,7 +104,7 @@ declare module '@reduxjs/toolkit' {
   }
 }
 
-/*#__PURE__*/ export function createEntityMethods<
+export function createEntityMethods<
   T,
   Id extends EntityId,
   State = EntityState<T, Id>,
@@ -181,7 +181,7 @@ declare module '@reduxjs/toolkit' {
 
 export const entityMethodsCreator: ReducerCreator<
   typeof entityMethodsCreatorType
-> = /*#__PURE__*/ {
+> = {
   type: entityMethodsCreatorType,
   create: createEntityMethods,
 }

From 9456b994a935b4d8627007206497be2932fc0f74 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.durrant@marketdojo.com>
Date: Mon, 5 Feb 2024 15:14:31 +0000
Subject: [PATCH 106/178] fix tree shaking by comparing type instead of
 identity

---
 packages/toolkit/src/createSlice.ts | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 21816d4753..089a09211a 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -983,7 +983,10 @@ export function buildCreateSlice<
       throw new Error(
         `Cannot use reserved creator type: ${String(creator.type)}`,
       )
-    } else if (name === 'asyncThunk' && creator !== asyncThunkCreator) {
+    } else if (
+      name === 'asyncThunk' &&
+      creator.type !== ReducerType.asyncThunk
+    ) {
       throw new Error(
         "If provided, `asyncThunk` creator must be `asyncThunkCreator` from '@reduxjs/toolkit'",
       )

From 711c8b4b4105127f63db10e78bac0e4d115dceff Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sat, 10 Feb 2024 23:56:57 +0000
Subject: [PATCH 107/178] remove todo

---
 packages/toolkit/src/createSlice.ts | 2 --
 1 file changed, 2 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 089a09211a..aae2134f17 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -243,7 +243,6 @@ interface ReducerHandlingContextMethods<State> {
    */
   exposeAction(
     name: string,
-    // TODO: see if there's a way to get the actual type cleanly
     actionCreator: unknown,
   ): ReducerHandlingContextMethods<State>
   /**
@@ -259,7 +258,6 @@ interface ReducerHandlingContextMethods<State> {
    */
   exposeCaseReducer(
     name: string,
-    // TODO: see if there's a way to get the actual type cleanly
     reducer: unknown,
   ): ReducerHandlingContextMethods<State>
   /**

From c8daec56b34fea4efca95a1386ef81016fa27111 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.durrant@marketdojo.com>
Date: Mon, 12 Feb 2024 12:29:55 +0000
Subject: [PATCH 108/178] Move type test to type-d file

---
 packages/toolkit/src/createSlice.ts           |   4 +-
 packages/toolkit/src/index.ts                 |   2 +
 .../toolkit/src/tests/createSlice.test-d.ts   | 109 ++++++++++++++++++
 .../toolkit/src/tests/createSlice.test.ts     |  10 --
 4 files changed, 113 insertions(+), 12 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index aae2134f17..d18f4c1ab5 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -195,7 +195,7 @@ interface ReducerHandlingContext<State> {
   actionCreators: Record<string, any>
 }
 
-interface ReducerHandlingContextMethods<State> {
+export interface ReducerHandlingContextMethods<State> {
   /**
    * Adds a case reducer to handle a single action type.
    * @param actionCreator - Either a plain action type string, or an action creator generated by [`createAction`](./createAction) that can be used to determine the action type.
@@ -267,7 +267,7 @@ interface ReducerHandlingContextMethods<State> {
   getInitialState(): State
 }
 
-interface ReducerDetails {
+export interface ReducerDetails {
   /** The key the reducer was defined under */
   reducerName: string
   /** The predefined action type, i.e. `${slice.name}/${reducerName}` */
diff --git a/packages/toolkit/src/index.ts b/packages/toolkit/src/index.ts
index 05fd217831..5e96705ce7 100644
--- a/packages/toolkit/src/index.ts
+++ b/packages/toolkit/src/index.ts
@@ -87,6 +87,8 @@ export type {
   ReducerNamesOfType,
   ReducerCreatorEntry,
   ReducerCreator,
+  ReducerDetails,
+  ReducerHandlingContextMethods,
   SliceActionType,
   CaseReducerDefinition,
   PreparedCaseReducerDefinition,
diff --git a/packages/toolkit/src/tests/createSlice.test-d.ts b/packages/toolkit/src/tests/createSlice.test-d.ts
index d87eb33fc3..1a0a323e9a 100644
--- a/packages/toolkit/src/tests/createSlice.test-d.ts
+++ b/packages/toolkit/src/tests/createSlice.test-d.ts
@@ -8,13 +8,19 @@ import type {
   ActionReducerMapBuilder,
   AsyncThunk,
   CaseReducer,
+  CreatorCaseReducers,
   PayloadAction,
   PayloadActionCreator,
   Reducer,
+  ReducerCreator,
+  ReducerCreatorEntry,
   ReducerCreators,
   ReducerDefinition,
+  ReducerHandlingContextMethods,
+  ReducerNamesOfType,
   SerializedError,
   SliceCaseReducers,
+  ThunkAction,
   ThunkDispatch,
   UnknownAction,
   ValidateSliceCaseReducers,
@@ -27,9 +33,12 @@ import {
   createAsyncThunk,
   createSlice,
   isRejected,
+  nanoid,
 } from '@reduxjs/toolkit'
 import { castDraft } from 'immer'
 
+const toasterCreatorType = Symbol()
+
 describe('type tests', () => {
   const counterSlice = createSlice({
     name: 'counter',
@@ -983,4 +992,104 @@ describe('type tests', () => {
       },
     })
   })
+  test('creators can disable themselves if state is incompatible', () => {
+    const toastCreator: ReducerCreator<typeof toasterCreatorType> = {
+      type: toasterCreatorType,
+      create: () => ({
+        _reducerDefinitionType: toasterCreatorType,
+      }),
+      handle({ type, reducerName }, _definition, context) {
+        const toastOpened = createAction<{ message: string; id: string }>(
+          type + '/opened',
+        )
+        const toastClosed = createAction<string>(type + '/closed')
+        function openToast(
+          ms: number,
+          message: string,
+        ): ThunkAction<void, unknown, unknown, UnknownAction> {
+          return (dispatch, getState) => {
+            const id = nanoid()
+            dispatch(toastOpened({ message, id }))
+            setTimeout(() => {
+              dispatch(toastClosed(id))
+            }, ms)
+          }
+        }
+        Object.assign(openToast, { toastOpened, toastClosed })
+        ;(context as ReducerHandlingContextMethods<ToastState>)
+          .addCase(toastOpened, (state, { payload: { message, id } }) => {
+            state.toasts[id] = { message }
+          })
+          .addCase(toastClosed, (state, action) => {
+            delete state.toasts[action.payload]
+          })
+          .exposeAction(reducerName, openToast)
+      },
+    }
+
+    const createAppSlice = buildCreateSlice({
+      creators: { toaster: toastCreator },
+    })
+
+    const toastSlice = createAppSlice({
+      name: 'toast',
+      initialState: { toasts: {} } as ToastState,
+      reducers: (create) => ({
+        toast: create.toaster(),
+      }),
+    })
+
+    expectTypeOf(toastSlice.actions.toast).toEqualTypeOf<AddToastThunk>()
+
+    expectTypeOf(toastSlice.actions.toast).toBeCallableWith(100, 'hello')
+
+    const incompatibleSlice = createAppSlice({
+      name: 'incompatible',
+      initialState: {},
+      reducers: (create) => {
+        expectTypeOf(create).not.toHaveProperty('toaster')
+        return {}
+      },
+    })
+  })
 })
+
+interface Toast {
+  message: string
+}
+
+interface ToastState {
+  toasts: Record<string, Toast>
+}
+
+interface AddToastThunk {
+  (
+    ms: number,
+    message: string,
+  ): ThunkAction<void, unknown, unknown, UnknownAction>
+  toastOpened: PayloadActionCreator<{ message: string; id: string }>
+  toastClosed: PayloadActionCreator<string>
+}
+
+declare module '@reduxjs/toolkit' {
+  export interface SliceReducerCreators<
+    State = any,
+    CaseReducers extends
+      CreatorCaseReducers<State> = CreatorCaseReducers<State>,
+    Name extends string = string,
+  > {
+    [toasterCreatorType]: ReducerCreatorEntry<
+      State extends ToastState
+        ? () => ReducerDefinition<typeof toasterCreatorType>
+        : never,
+      {
+        actions: {
+          [ReducerName in ReducerNamesOfType<
+            CaseReducers,
+            typeof toasterCreatorType
+          >]: AddToastThunk
+        }
+      }
+    >
+  }
+}
diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index 28097bcbb6..637ffa6357 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -1200,16 +1200,6 @@ describe('createSlice', () => {
         store.dispatch(reset())
         expect(selectValue(store.getState())).toBe(1)
       })
-      test.skip('creators can discourage their use if state is incompatible (types only)', () => {
-        const counterSlice = createAppSlice({
-          name: 'counter',
-          initialState: { value: 0 },
-          reducers: (create) => ({
-            // @ts-expect-error incompatible state
-            ...create.historyMethods(),
-          }),
-        })
-      })
     })
   })
 })

From 8c1c6f08bf5ab8806efea712c73db1063e188bb1 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.durrant@marketdojo.com>
Date: Mon, 12 Feb 2024 12:36:40 +0000
Subject: [PATCH 109/178] strongly type action types

---
 .../toolkit/src/tests/createSlice.test-d.ts   | 25 ++++++++++----
 .../toolkit/src/tests/createSlice.test.ts     | 33 ++++++++++---------
 2 files changed, 37 insertions(+), 21 deletions(-)

diff --git a/packages/toolkit/src/tests/createSlice.test-d.ts b/packages/toolkit/src/tests/createSlice.test-d.ts
index 1a0a323e9a..edd4f9765d 100644
--- a/packages/toolkit/src/tests/createSlice.test-d.ts
+++ b/packages/toolkit/src/tests/createSlice.test-d.ts
@@ -19,6 +19,7 @@ import type {
   ReducerHandlingContextMethods,
   ReducerNamesOfType,
   SerializedError,
+  SliceActionType,
   SliceCaseReducers,
   ThunkAction,
   ThunkDispatch,
@@ -1032,17 +1033,23 @@ describe('type tests', () => {
     })
 
     const toastSlice = createAppSlice({
-      name: 'toast',
+      name: 'toasts',
       initialState: { toasts: {} } as ToastState,
       reducers: (create) => ({
         toast: create.toaster(),
       }),
     })
 
-    expectTypeOf(toastSlice.actions.toast).toEqualTypeOf<AddToastThunk>()
+    expectTypeOf(toastSlice.actions.toast).toEqualTypeOf<
+      AddToastThunk<'toasts', 'toast'>
+    >()
 
     expectTypeOf(toastSlice.actions.toast).toBeCallableWith(100, 'hello')
 
+    expectTypeOf(
+      toastSlice.actions.toast.toastOpened.type,
+    ).toEqualTypeOf<'toasts/toast/opened'>()
+
     const incompatibleSlice = createAppSlice({
       name: 'incompatible',
       initialState: {},
@@ -1062,13 +1069,19 @@ interface ToastState {
   toasts: Record<string, Toast>
 }
 
-interface AddToastThunk {
+interface AddToastThunk<Name extends string, ReducerName extends PropertyKey> {
   (
     ms: number,
     message: string,
   ): ThunkAction<void, unknown, unknown, UnknownAction>
-  toastOpened: PayloadActionCreator<{ message: string; id: string }>
-  toastClosed: PayloadActionCreator<string>
+  toastOpened: PayloadActionCreator<
+    { message: string; id: string },
+    `${SliceActionType<Name, ReducerName>}/opened`
+  >
+  toastClosed: PayloadActionCreator<
+    string,
+    `${SliceActionType<Name, ReducerName>}/closed`
+  >
 }
 
 declare module '@reduxjs/toolkit' {
@@ -1087,7 +1100,7 @@ declare module '@reduxjs/toolkit' {
           [ReducerName in ReducerNamesOfType<
             CaseReducers,
             typeof toasterCreatorType
-          >]: AddToastThunk
+          >]: AddToastThunk<Name, ReducerName>
         }
       }
     >
diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index 637ffa6357..33a0f095bc 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -1210,6 +1210,23 @@ interface LoaderReducerDefinition<State>
   ended?: CaseReducer<State, PayloadAction<string>>
 }
 
+interface LoaderThunk<Name extends string, ReducerName extends PropertyKey> {
+  (): ThunkAction<
+    { loaderId: string; end: () => void },
+    unknown,
+    unknown,
+    Action
+  >
+  started: PayloadActionCreator<
+    string,
+    `${SliceActionType<Name, ReducerName>}/started`
+  >
+  ended: PayloadActionCreator<
+    string,
+    `${SliceActionType<Name, ReducerName>}/ended`
+  >
+}
+
 interface PatchesState {
   undo: Patch[]
   redo: Patch[]
@@ -1241,21 +1258,7 @@ declare module '@reduxjs/toolkit' {
           [ReducerName in ReducerNamesOfType<
             CaseReducers,
             typeof loaderCreatorType
-          >]: (() => ThunkAction<
-            { loaderId: string; end: () => void },
-            unknown,
-            unknown,
-            Action
-          >) & {
-            started: PayloadActionCreator<
-              string,
-              `${SliceActionType<Name, ReducerName>}/started`
-            >
-            ended: PayloadActionCreator<
-              string,
-              `${SliceActionType<Name, ReducerName>}/ended`
-            >
-          }
+          >]: LoaderThunk<Name, ReducerName>
         }
         caseReducers: {
           [ReducerName in ReducerNamesOfType<

From 97fa0bd1b99cdbd88664c452afe25c9f6ae82a66 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.durrant@marketdojo.com>
Date: Mon, 12 Feb 2024 12:59:27 +0000
Subject: [PATCH 110/178] remove defaults from SliceReducerCreator

---
 docs/api/createSlice.mdx                      | 70 ++++++++-----------
 packages/toolkit/src/createSlice.ts           | 49 ++++++++-----
 .../toolkit/src/entities/slice_creator.ts     |  7 +-
 .../toolkit/src/tests/createSlice.test-d.ts   |  7 +-
 .../toolkit/src/tests/createSlice.test.ts     |  7 +-
 5 files changed, 73 insertions(+), 67 deletions(-)

diff --git a/docs/api/createSlice.mdx b/docs/api/createSlice.mdx
index 9f7a463c7b..095ab45baa 100644
--- a/docs/api/createSlice.mdx
+++ b/docs/api/createSlice.mdx
@@ -1115,10 +1115,9 @@ const reducerCreatorType = Symbol()
 
 declare module '@reduxjs/toolkit' {
   export interface SliceReducerCreators<
-    State = any,
-    CaseReducers extends
-      CreatorCaseReducers<State> = CreatorCaseReducers<State>,
-    Name extends string = string,
+    State,
+    CaseReducers extends CreatorCaseReducers<State>,
+    Name extends string,
   > {
     [reducerCreatorType]: ReducerCreatorEntry<
       () => ReducerDefinition<typeof reducerCreatorType>
@@ -1144,10 +1143,9 @@ const batchedCreatorType = Symbol()
 
 declare module '@reduxjs/toolkit' {
   export interface SliceReducerCreators<
-    State = any,
-    CaseReducers extends
-      CreatorCaseReducers<State> = CreatorCaseReducers<State>,
-    Name extends string = string,
+    State,
+    CaseReducers extends CreatorCaseReducers<State>,
+    Name extends string,
   > {
     [batchedCreatorType]: ReducerCreatorEntry<
       <Payload>(
@@ -1183,10 +1181,9 @@ const loaderCreatorType = Symbol()
 
 declare module '@reduxjs/toolkit' {
   export interface SliceReducerCreators<
-    State = any,
-    CaseReducers extends
-      CreatorCaseReducers<State> = CreatorCaseReducers<State>,
-    Name extends string = string,
+    State,
+    CaseReducers extends CreatorCaseReducers<State>,
+    Name extends string,
   > {
     [loaderCreatorType]: ReducerCreatorEntry<
       // highlight-next-line
@@ -1210,10 +1207,9 @@ const loaderCreatorType = Symbol()
 
 declare module '@reduxjs/toolkit' {
   export interface SliceReducerCreators<
-    State = any,
-    CaseReducers extends
-      CreatorCaseReducers<State> = CreatorCaseReducers<State>,
-    Name extends string = string,
+    State,
+    CaseReducers extends CreatorCaseReducers<State>,
+    Name extends string,
   > {
     [loaderCreatorType]: ReducerCreatorEntry<
       () => {
@@ -1246,10 +1242,9 @@ const asyncThunkCreatorType = Symbol()
 
 declare module '@reduxjs/toolkit' {
   export interface SliceReducerCreators<
-    State = any,
-    CaseReducers extends
-      CreatorCaseReducers<State> = CreatorCaseReducers<State>,
-    Name extends string = string,
+    State,
+    CaseReducers extends CreatorCaseReducers<State>,
+    Name extends string,
   > {
     [asyncThunkCreatorType]: ReducerCreatorEntry<
       <ThunkArg, Returned>(
@@ -1287,10 +1282,9 @@ const preparedReducerType = Symbol()
 
 declare module '@reduxjs/toolkit' {
   export interface SliceReducerCreators<
-    State = any,
-    CaseReducers extends
-      CreatorCaseReducers<State> = CreatorCaseReducers<State>,
-    Name extends string = string,
+    State,
+    CaseReducers extends CreatorCaseReducers<State>,
+    Name extends string,
   > {
     [preparedReducerType]: ReducerCreatorEntry<
       <Prepare extends PrepareAction<any>>(
@@ -1392,10 +1386,9 @@ interface ToastThunkCreator<
 // register the creator types
 declare module '@reduxjs/toolkit' {
   export interface SliceReducerCreators<
-    State = any,
-    CaseReducers extends
-      CreatorCaseReducers<State> = CreatorCaseReducers<State>,
-    Name extends string = string,
+    State,
+    CaseReducers extends CreatorCaseReducers<State>,
+    Name extends string,
   > {
     [toastCreatorType]: ReducerCreatorEntry<
       (
@@ -1522,10 +1515,9 @@ interface PaginationState {
 
 declare module '@reduxjs/toolkit' {
   export interface SliceReducerCreators<
-    State = any,
-    CaseReducers extends
-      CreatorCaseReducers<State> = CreatorCaseReducers<State>,
-    Name extends string = string,
+    State,
+    CaseReducers extends CreatorCaseReducers<State>,
+    Name extends string,
   > {
     [paginationCreatorType]: ReducerCreatorEntry<
       // make sure the creator is only called when state is compatible
@@ -1593,9 +1585,10 @@ interface HistoryState<T> {
 
 declare module '@reduxjs/toolkit' {
   export interface SliceReducerCreators<
-    State = any,
-    CaseReducers extends CreatorCaseReducers<State> = CreatorCaseReducers<State>,
-    Name extends string = string
+    State,
+    CaseReducers extends
+      CreatorCaseReducers<State>,
+    Name extends string,
   > {
     [paginationCreatorType]: ReducerCreatorEntry<
       // make sure the creator is only called when state is compatibleState extends HistoryState<unknown>
@@ -1702,10 +1695,9 @@ interface UndoableMeta {
 
 declare module '@reduxjs/toolkit' {
   export interface SliceReducerCreators<
-    State = any,
-    CaseReducers extends
-      CreatorCaseReducers<State> = CreatorCaseReducers<State>,
-    Name extends string = string,
+    State,
+    CaseReducers extends CreatorCaseReducers<State>,
+    Name extends string,
   > {
     [undoableCreatorType]: ReducerCreatorEntry<
       State extends HistoryState<infer Data>
diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index d18f4c1ab5..9b237e5b80 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -39,7 +39,7 @@ export enum ReducerType {
   asyncThunk = 'asyncThunk',
 }
 
-export type RegisteredReducerType = keyof SliceReducerCreators
+export type RegisteredReducerType = keyof SliceReducerCreators<any, any, any>
 
 export interface ReducerDefinition<
   T extends RegisteredReducerType = RegisteredReducerType,
@@ -68,9 +68,9 @@ export type CreatorCaseReducers<State> =
   | SliceCaseReducers<State>
 
 export interface SliceReducerCreators<
-  State = any,
-  CaseReducers extends CreatorCaseReducers<State> = CreatorCaseReducers<State>,
-  Name extends string = string,
+  State,
+  CaseReducers extends CreatorCaseReducers<State>,
+  Name extends string,
 > {
   [ReducerType.reducer]: ReducerCreatorEntry<
     {
@@ -179,12 +179,20 @@ export type ReducerCreators<
   State,
   CreatorMap extends Record<string, RegisteredReducerType> = {},
 > = {
-  reducer: SliceReducerCreators<State>[ReducerType.reducer]['create']
-  preparedReducer: SliceReducerCreators<State>[ReducerType.reducerWithPrepare]['create']
+  reducer: SliceReducerCreators<State, any, any>[ReducerType.reducer]['create']
+  preparedReducer: SliceReducerCreators<
+    State,
+    any,
+    any
+  >[ReducerType.reducerWithPrepare]['create']
 } & {
-  [Name in keyof CreatorMap as SliceReducerCreators<State>[CreatorMap[Name]]['create'] extends never
+  [Name in keyof CreatorMap as SliceReducerCreators<
+    State,
+    any,
+    any
+  >[CreatorMap[Name]]['create'] extends never
     ? never
-    : Name]: SliceReducerCreators<State>[CreatorMap[Name]]['create']
+    : Name]: SliceReducerCreators<State, any, any>[CreatorMap[Name]]['create']
 }
 
 interface ReducerHandlingContext<State> {
@@ -289,23 +297,31 @@ type RecursiveExtractDefinition<
       : never)
 
 type ReducerDefinitionsForType<Type extends RegisteredReducerType> = {
-  [CreatorType in keyof SliceReducerCreators]:
+  [CreatorType in RegisteredReducerType]:
     | RecursiveExtractDefinition<
-        ReturnType<SliceReducerCreators[CreatorType]['create']>,
+        ReturnType<SliceReducerCreators<any, any, any>[CreatorType]['create']>,
         Type
       >
     | {
-        [K in keyof SliceReducerCreators[CreatorType]['create']]: SliceReducerCreators[CreatorType]['create'][K] extends (
+        [K in keyof SliceReducerCreators<
+          any,
+          any,
+          any
+        >[CreatorType]['create']]: SliceReducerCreators<
+          any,
+          any,
+          any
+        >[CreatorType]['create'][K] extends (
           ...args: any[]
         ) => infer Definitions
           ? RecursiveExtractDefinition<Definitions, Type>
           : never
-      }[keyof SliceReducerCreators[CreatorType]['create']]
-}[keyof SliceReducerCreators]
+      }[keyof SliceReducerCreators<any, any, any>[CreatorType]['create']]
+}[keyof SliceReducerCreators<any, any, any>]
 
 export type ReducerCreator<Type extends RegisteredReducerType> = {
   type: Type
-  create: SliceReducerCreators[Type]['create']
+  create: SliceReducerCreators<any, any, any>[Type]['create']
 } & (ReducerDefinitionsForType<Type> extends never
   ? {}
   : {
@@ -368,7 +384,7 @@ export interface Slice<
    * The individual case reducer functions that were passed in the `reducers` parameter.
    * This enables reuse and testing if they were defined inline when calling `createSlice`.
    */
-  caseReducers: SliceDefinedCaseReducers<CaseReducers>
+  caseReducers: SliceDefinedCaseReducers<CaseReducers, Name>
 
   /**
    * Provides access to the initial state value given to the slice.
@@ -778,12 +794,13 @@ type SliceDefinedCaseReducers<
   CaseReducers extends
     | SliceCaseReducers<any>
     | Record<string, ReducerDefinition>,
+  SliceName extends string = string,
 > = Id<
   UnionToIntersection<
     SliceReducerCreators<
       any,
       CaseReducers,
-      any
+      SliceName
     >[RegisteredReducerType]['caseReducers']
   >
 >
diff --git a/packages/toolkit/src/entities/slice_creator.ts b/packages/toolkit/src/entities/slice_creator.ts
index addcd49284..d15c390ae1 100644
--- a/packages/toolkit/src/entities/slice_creator.ts
+++ b/packages/toolkit/src/entities/slice_creator.ts
@@ -95,10 +95,9 @@ type EntityMethodsCreator<State> =
 
 declare module '@reduxjs/toolkit' {
   export interface SliceReducerCreators<
-    State = any,
-    CaseReducers extends
-      CreatorCaseReducers<State> = CreatorCaseReducers<State>,
-    Name extends string = string,
+    State,
+    CaseReducers extends CreatorCaseReducers<State>,
+    Name extends string,
   > {
     [entityMethodsCreatorType]: ReducerCreatorEntry<EntityMethodsCreator<State>>
   }
diff --git a/packages/toolkit/src/tests/createSlice.test-d.ts b/packages/toolkit/src/tests/createSlice.test-d.ts
index edd4f9765d..83c51a4dcf 100644
--- a/packages/toolkit/src/tests/createSlice.test-d.ts
+++ b/packages/toolkit/src/tests/createSlice.test-d.ts
@@ -1086,10 +1086,9 @@ interface AddToastThunk<Name extends string, ReducerName extends PropertyKey> {
 
 declare module '@reduxjs/toolkit' {
   export interface SliceReducerCreators<
-    State = any,
-    CaseReducers extends
-      CreatorCaseReducers<State> = CreatorCaseReducers<State>,
-    Name extends string = string,
+    State,
+    CaseReducers extends CreatorCaseReducers<State>,
+    Name extends string,
   > {
     [toasterCreatorType]: ReducerCreatorEntry<
       State extends ToastState
diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index 33a0f095bc..95f2462eb1 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -1244,10 +1244,9 @@ interface UndoableOptions {
 
 declare module '@reduxjs/toolkit' {
   export interface SliceReducerCreators<
-    State = any,
-    CaseReducers extends
-      CreatorCaseReducers<State> = CreatorCaseReducers<State>,
-    Name extends string = string,
+    State,
+    CaseReducers extends CreatorCaseReducers<State>,
+    Name extends string,
   > {
     [loaderCreatorType]: ReducerCreatorEntry<
       (

From c0ba6abf352334a8cf552b9b2c99db597f873677 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.durrant@marketdojo.com>
Date: Mon, 12 Feb 2024 13:02:49 +0000
Subject: [PATCH 111/178] pass name and state to SliceReducerCreators when
 getting final actions/case reducers type

---
 packages/toolkit/src/createSlice.ts | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 9b237e5b80..dc34040d6b 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -378,13 +378,13 @@ export interface Slice<
    * Action creators for the types of actions that are handled by the slice
    * reducer.
    */
-  actions: CaseReducerActions<CaseReducers, Name>
+  actions: CaseReducerActions<CaseReducers, Name, State>
 
   /**
    * The individual case reducer functions that were passed in the `reducers` parameter.
    * This enables reuse and testing if they were defined inline when calling `createSlice`.
    */
-  caseReducers: SliceDefinedCaseReducers<CaseReducers, Name>
+  caseReducers: SliceDefinedCaseReducers<CaseReducers, Name, State>
 
   /**
    * Provides access to the initial state value given to the slice.
@@ -750,10 +750,11 @@ export type CaseReducerActions<
     | SliceCaseReducers<any>
     | Record<string, ReducerDefinition>,
   SliceName extends string,
+  State = any,
 > = Id<
   UnionToIntersection<
     SliceReducerCreators<
-      any,
+      State,
       CaseReducers,
       SliceName
     >[RegisteredReducerType]['actions']
@@ -795,10 +796,11 @@ type SliceDefinedCaseReducers<
     | SliceCaseReducers<any>
     | Record<string, ReducerDefinition>,
   SliceName extends string = string,
+  State = any,
 > = Id<
   UnionToIntersection<
     SliceReducerCreators<
-      any,
+      State,
       CaseReducers,
       SliceName
     >[RegisteredReducerType]['caseReducers']

From 01feaaece6d25bceb9bad30af0c78f72eb44feb5 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.durrant@marketdojo.com>
Date: Mon, 12 Feb 2024 13:04:42 +0000
Subject: [PATCH 112/178] use CreatorCaseReducers type in more places

---
 packages/toolkit/src/createSlice.ts | 16 ++++------------
 1 file changed, 4 insertions(+), 12 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index dc34040d6b..b11948a5b5 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -352,9 +352,7 @@ interface InjectIntoConfig<NewReducerPath extends string> extends InjectConfig {
  */
 export interface Slice<
   State = any,
-  CaseReducers extends
-    | SliceCaseReducers<State>
-    | Record<string, ReducerDefinition> = SliceCaseReducers<State>,
+  CaseReducers extends CreatorCaseReducers<State> = SliceCaseReducers<State>,
   Name extends string = string,
   ReducerPath extends string = Name,
   Selectors extends SliceSelectors<State> = SliceSelectors<State>,
@@ -442,9 +440,7 @@ export interface Slice<
  */
 interface InjectedSlice<
   State = any,
-  CaseReducers extends
-    | SliceCaseReducers<State>
-    | Record<string, ReducerDefinition> = SliceCaseReducers<State>,
+  CaseReducers extends CreatorCaseReducers<State> = SliceCaseReducers<State>,
   Name extends string = string,
   ReducerPath extends string = Name,
   Selectors extends SliceSelectors<State> = SliceSelectors<State>,
@@ -746,9 +742,7 @@ export type SliceActionType<
  * @public
  */
 export type CaseReducerActions<
-  CaseReducers extends
-    | SliceCaseReducers<any>
-    | Record<string, ReducerDefinition>,
+  CaseReducers extends CreatorCaseReducers<State>,
   SliceName extends string,
   State = any,
 > = Id<
@@ -792,9 +786,7 @@ type ActionCreatorForCaseReducer<CR, Type extends string> = CR extends (
  * @internal
  */
 type SliceDefinedCaseReducers<
-  CaseReducers extends
-    | SliceCaseReducers<any>
-    | Record<string, ReducerDefinition>,
+  CaseReducers extends CreatorCaseReducers<State>,
   SliceName extends string = string,
   State = any,
 > = Id<

From 8af2b0087412f53ee1b4ef2d95bcac7ba119bdb5 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.durrant@marketdojo.com>
Date: Mon, 12 Feb 2024 13:17:22 +0000
Subject: [PATCH 113/178] cast harder

---
 packages/toolkit/src/tests/createSlice.test-d.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/toolkit/src/tests/createSlice.test-d.ts b/packages/toolkit/src/tests/createSlice.test-d.ts
index 83c51a4dcf..8519ef0a45 100644
--- a/packages/toolkit/src/tests/createSlice.test-d.ts
+++ b/packages/toolkit/src/tests/createSlice.test-d.ts
@@ -1017,7 +1017,7 @@ describe('type tests', () => {
           }
         }
         Object.assign(openToast, { toastOpened, toastClosed })
-        ;(context as ReducerHandlingContextMethods<ToastState>)
+        ;(context as any as ReducerHandlingContextMethods<ToastState>)
           .addCase(toastOpened, (state, { payload: { message, id } }) => {
             state.toasts[id] = { message }
           })

From c8af945a539a989671334c05099a6f643fef6d84 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.durrant@marketdojo.com>
Date: Mon, 12 Feb 2024 13:26:34 +0000
Subject: [PATCH 114/178] add notes about type parameters

---
 docs/api/createSlice.mdx | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/docs/api/createSlice.mdx b/docs/api/createSlice.mdx
index 095ab45baa..ef5e5a925d 100644
--- a/docs/api/createSlice.mdx
+++ b/docs/api/createSlice.mdx
@@ -1126,12 +1126,24 @@ declare module '@reduxjs/toolkit' {
 }
 ```
 
+The type parameters for `SliceReducerCreators` are:
+
+- `State` - The state type used by the slice.
+- `CaseReducers` - The case reducer definitions returned by the creator callback.
+- `Name` - The [`name`](#name) used by the slice.
+
 The `ReducerCreatorEntry<Create, Exposes>` utility has two type parameters:
 
 ##### `Create`
 
 The signature of the `create` method of the creator definition.
 
+:::caution `CaseReducers` and `Name`
+
+Your `Create` type should not depend on the `CaseReducers` and `Name` type parameters, as these will not yet exist when the creator is being called.
+
+:::
+
 :::tip Using `this` to access other creators
 
 Assuming the creator is called as `create.yourCreator()`, the `this` value for the function is the `create` object - meaning you can call other creators on the same object.
@@ -1223,7 +1235,7 @@ declare module '@reduxjs/toolkit' {
 
 :::
 
-##### `Exposes`
+##### `Exposes` (optional)
 
 The second type parameter for `ReducerCreatorEntry` is optional, but should be used if the creator will handle some reducer definitions itself. It indicates what actions and case reducers will be attached to the slice, and is used to determine the final types of `slice.actions` and `slice.caseReducers`.
 

From 7118f1ac252b228d43ddd872f22ace6a51acbdf9 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.durrant@marketdojo.com>
Date: Mon, 12 Feb 2024 13:45:05 +0000
Subject: [PATCH 115/178] remove unused barrel file

---
 packages/toolkit/src/entities/index.ts | 9 ---------
 1 file changed, 9 deletions(-)
 delete mode 100644 packages/toolkit/src/entities/index.ts

diff --git a/packages/toolkit/src/entities/index.ts b/packages/toolkit/src/entities/index.ts
deleted file mode 100644
index 1c2e13834f..0000000000
--- a/packages/toolkit/src/entities/index.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-export { createEntityAdapter } from './create_adapter'
-export type {
-  EntityState,
-  EntityAdapter,
-  Update,
-  IdSelector,
-  Comparer,
-} from './models'
-export { entityMethodsCreator, entityMethodsCreatorType } from './slice_creator'

From ca7b1dafdc76f7b40d4c0ee659ad8bf0e9109fe2 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.durrant@marketdojo.com>
Date: Mon, 12 Feb 2024 15:55:44 +0000
Subject: [PATCH 116/178] fix imports in tests

---
 packages/toolkit/src/entities/tests/entity_state.test.ts    | 4 ++--
 packages/toolkit/src/entities/tests/state_adapter.test.ts   | 4 ++--
 packages/toolkit/src/entities/tests/state_selectors.test.ts | 4 ++--
 3 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/packages/toolkit/src/entities/tests/entity_state.test.ts b/packages/toolkit/src/entities/tests/entity_state.test.ts
index 65accf475c..f9814a411f 100644
--- a/packages/toolkit/src/entities/tests/entity_state.test.ts
+++ b/packages/toolkit/src/entities/tests/entity_state.test.ts
@@ -1,5 +1,5 @@
-import type { EntityAdapter } from '../index'
-import { createEntityAdapter } from '../index'
+import type { EntityAdapter } from '../models'
+import { createEntityAdapter } from '../create_adapter'
 import type { PayloadAction } from '../../createAction'
 import { createAction } from '../../createAction'
 import { createSlice } from '../../createSlice'
diff --git a/packages/toolkit/src/entities/tests/state_adapter.test.ts b/packages/toolkit/src/entities/tests/state_adapter.test.ts
index a05b715ff9..f691639b23 100644
--- a/packages/toolkit/src/entities/tests/state_adapter.test.ts
+++ b/packages/toolkit/src/entities/tests/state_adapter.test.ts
@@ -1,5 +1,5 @@
-import type { EntityAdapter } from '../index'
-import { createEntityAdapter } from '../index'
+import type { EntityAdapter } from '../models'
+import { createEntityAdapter } from '../create_adapter'
 import type { PayloadAction } from '../../createAction'
 import { configureStore } from '../../configureStore'
 import { createSlice } from '../../createSlice'
diff --git a/packages/toolkit/src/entities/tests/state_selectors.test.ts b/packages/toolkit/src/entities/tests/state_selectors.test.ts
index 3afba41ac6..dba56cc01d 100644
--- a/packages/toolkit/src/entities/tests/state_selectors.test.ts
+++ b/packages/toolkit/src/entities/tests/state_selectors.test.ts
@@ -1,6 +1,6 @@
 import { createDraftSafeSelectorCreator } from '../../createDraftSafeSelector'
-import type { EntityAdapter, EntityState } from '../index'
-import { createEntityAdapter } from '../index'
+import type { EntityAdapter, EntityState } from '../models'
+import { createEntityAdapter } from '../create_adapter'
 import type { EntitySelectors } from '../models'
 import type { BookModel } from './fixtures/book'
 import { AClockworkOrange, AnimalFarm, TheGreatGatsby } from './fixtures/book'

From 96b3084d17122a4ad07225f6a74239aa0593b22b Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.durrant@marketdojo.com>
Date: Mon, 12 Feb 2024 17:42:17 +0000
Subject: [PATCH 117/178] add named selectors

---
 packages/toolkit/src/entities/models.ts       | 70 +++++++++++---
 .../toolkit/src/entities/slice_creator.ts     | 31 +------
 .../toolkit/src/entities/state_selectors.ts   | 92 ++++++++++++++-----
 .../entities/tests/state_selectors.test.ts    | 55 +++++++++++
 4 files changed, 185 insertions(+), 63 deletions(-)

diff --git a/packages/toolkit/src/entities/models.ts b/packages/toolkit/src/entities/models.ts
index a7ec0238d9..b39d679eba 100644
--- a/packages/toolkit/src/entities/models.ts
+++ b/packages/toolkit/src/entities/models.ts
@@ -3,6 +3,8 @@ import type { Draft } from 'immer'
 import type { PayloadAction } from '../createAction'
 import type { GetSelectorsOptions } from './state_selectors'
 import type { CastAny, Id as Compute } from '../tsHelpers'
+import type { CaseReducerDefinition } from '../createSlice'
+import type { CaseReducer } from '../createReducer'
 
 /**
  * @public
@@ -158,12 +160,49 @@ export interface EntityStateAdapter<T, Id extends EntityId> {
 /**
  * @public
  */
-export interface EntitySelectors<T, V, Id extends EntityId> {
-  selectIds: (state: V) => Id[]
-  selectEntities: (state: V) => Record<Id, T>
-  selectAll: (state: V) => T[]
-  selectTotal: (state: V) => number
-  selectById: (state: V, id: Id) => Compute<UncheckedIndexedAccess<T>>
+export type EntitySelectors<
+  T,
+  V,
+  Id extends EntityId,
+  Single extends string = '',
+  Plural extends string = DefaultPlural<Single>,
+> = {
+  [K in `select${Capitalize<Single>}Ids`]: (state: V) => Id[]
+} & {
+  [K in `select${Capitalize<Single>}Entities`]: (state: V) => Record<Id, T>
+} & {
+  [K in `selectAll${Capitalize<Plural>}`]: (state: V) => T[]
+} & {
+  [K in `selectTotal${Capitalize<Plural>}`]: (state: V) => number
+} & {
+  [K in `select${Capitalize<Single>}ById`]: (
+    state: V,
+    id: Id,
+  ) => Compute<UncheckedIndexedAccess<T>>
+}
+
+export type DefaultPlural<Single extends string> = Single extends ''
+  ? ''
+  : `${Single}s`
+
+export type EntityReducers<
+  T,
+  Id extends EntityId,
+  State = EntityState<T, Id>,
+  Single extends string = '',
+  Plural extends string = DefaultPlural<Single>,
+> = {
+  [K in keyof EntityStateAdapter<
+    T,
+    Id
+  > as `${K}${Capitalize<K extends `${string}One` ? Single : Plural>}`]: EntityStateAdapter<
+    T,
+    Id
+  >[K] extends (state: any) => any
+    ? CaseReducerDefinition<State, PayloadAction>
+    : EntityStateAdapter<T, Id>[K] extends CaseReducer<any, infer A>
+      ? CaseReducerDefinition<State, A>
+      : never
 }
 
 /**
@@ -175,12 +214,19 @@ export interface EntityAdapter<T, Id extends EntityId>
   sortComparer: false | Comparer<T>
   getInitialState(): EntityState<T, Id>
   getInitialState<S extends object>(state: S): EntityState<T, Id> & S
-  getSelectors(
+  getSelectors<
+    Single extends string = '',
+    Plural extends string = DefaultPlural<Single>,
+  >(
     selectState?: undefined,
-    options?: GetSelectorsOptions,
-  ): EntitySelectors<T, EntityState<T, Id>, Id>
-  getSelectors<V>(
+    options?: GetSelectorsOptions<Single, Plural>,
+  ): EntitySelectors<T, EntityState<T, Id>, Id, Single, Plural>
+  getSelectors<
+    V,
+    Single extends string = '',
+    Plural extends string = DefaultPlural<Single>,
+  >(
     selectState: (state: V) => EntityState<T, Id>,
-    options?: GetSelectorsOptions,
-  ): EntitySelectors<T, V, Id>
+    options?: GetSelectorsOptions<Single, Plural>,
+  ): EntitySelectors<T, V, Id, Single, Plural>
 }
diff --git a/packages/toolkit/src/entities/slice_creator.ts b/packages/toolkit/src/entities/slice_creator.ts
index d15c390ae1..179eeff07f 100644
--- a/packages/toolkit/src/entities/slice_creator.ts
+++ b/packages/toolkit/src/entities/slice_creator.ts
@@ -1,47 +1,22 @@
 import type {
-  CaseReducer,
   CreatorCaseReducers,
   ReducerCreator,
   ReducerCreatorEntry,
 } from '@reduxjs/toolkit'
-import type { PayloadAction } from '../createAction'
-import { reducerCreator, type CaseReducerDefinition } from '../createSlice'
+import { reducerCreator } from '../createSlice'
 import type { WithRequiredProp } from '../tsHelpers'
 import type {
   Update,
   EntityAdapter,
   EntityId,
   EntityState,
-  EntityStateAdapter,
+  DefaultPlural,
+  EntityReducers,
 } from './models'
 import { capitalize } from './utils'
 
 export const entityMethodsCreatorType = /*@__PURE__*/ Symbol()
 
-type DefaultPlural<Single extends string> = Single extends ''
-  ? ''
-  : `${Single}s`
-
-type EntityReducers<
-  T,
-  Id extends EntityId,
-  State = EntityState<T, Id>,
-  Single extends string = '',
-  Plural extends string = DefaultPlural<Single>,
-> = {
-  [K in keyof EntityStateAdapter<
-    T,
-    Id
-  > as `${K}${Capitalize<K extends `${string}One` ? Single : Plural>}`]: EntityStateAdapter<
-    T,
-    Id
-  >[K] extends (state: any) => any
-    ? CaseReducerDefinition<State, PayloadAction>
-    : EntityStateAdapter<T, Id>[K] extends CaseReducer<any, infer A>
-      ? CaseReducerDefinition<State, A>
-      : never
-}
-
 export interface EntityMethodsCreatorConfig<
   T,
   Id extends EntityId,
diff --git a/packages/toolkit/src/entities/state_selectors.ts b/packages/toolkit/src/entities/state_selectors.ts
index bd64cf001b..eb425aa223 100644
--- a/packages/toolkit/src/entities/state_selectors.ts
+++ b/packages/toolkit/src/entities/state_selectors.ts
@@ -1,6 +1,12 @@
 import type { CreateSelectorFunction, Selector, createSelector } from 'reselect'
 import { createDraftSafeSelector } from '../createDraftSafeSelector'
-import type { EntityState, EntitySelectors, EntityId } from './models'
+import type {
+  EntityState,
+  EntitySelectors,
+  EntityId,
+  DefaultPlural,
+} from './models'
+import { capitalize } from './utils'
 
 type AnyFunction = (...args: any) => any
 type AnyCreateSelectorFunction = CreateSelectorFunction<
@@ -8,25 +14,43 @@ type AnyCreateSelectorFunction = CreateSelectorFunction<
   <F extends AnyFunction>(f: F) => F
 >
 
-export interface GetSelectorsOptions {
+export interface GetSelectorsOptions<
+  Single extends string = '',
+  Plural extends string = DefaultPlural<''>,
+> {
   createSelector?: AnyCreateSelectorFunction
+  name?: Single
+  pluralName?: Plural
 }
 
 export function createSelectorsFactory<T, Id extends EntityId>() {
-  function getSelectors(
+  function getSelectors<
+    Single extends string = '',
+    Plural extends string = DefaultPlural<Single>,
+  >(
     selectState?: undefined,
-    options?: GetSelectorsOptions,
-  ): EntitySelectors<T, EntityState<T, Id>, Id>
-  function getSelectors<V>(
+    options?: GetSelectorsOptions<Single, Plural>,
+  ): EntitySelectors<T, EntityState<T, Id>, Id, Single, Plural>
+  function getSelectors<
+    V,
+    Single extends string = '',
+    Plural extends string = DefaultPlural<Single>,
+  >(
     selectState: (state: V) => EntityState<T, Id>,
-    options?: GetSelectorsOptions,
-  ): EntitySelectors<T, V, Id>
-  function getSelectors<V>(
+    options?: GetSelectorsOptions<Single, Plural>,
+  ): EntitySelectors<T, V, Id, Single, Plural>
+  function getSelectors<
+    V,
+    Single extends string = '',
+    Plural extends string = DefaultPlural<Single>,
+  >(
     selectState?: (state: V) => EntityState<T, Id>,
-    options: GetSelectorsOptions = {},
-  ): EntitySelectors<T, any, Id> {
+    options: GetSelectorsOptions<Single, Plural> = {},
+  ): EntitySelectors<T, any, Id, Single, Plural> {
     const {
       createSelector = createDraftSafeSelector as AnyCreateSelectorFunction,
+      name = '',
+      pluralName = name && `${name}s`,
     } = options
 
     const selectIds = (state: EntityState<T, Id>) => state.ids
@@ -45,14 +69,25 @@ export function createSelectorsFactory<T, Id extends EntityId>() {
 
     const selectTotal = createSelector(selectIds, (ids) => ids.length)
 
+    // template literal computed keys don't keep their type if there's an unresolved generic
+    // so we cast to some intermediate type to at least check we're using the right variables in the right places
+
+    const single = name as 's'
+    const plural = pluralName as 'p'
+
     if (!selectState) {
-      return {
-        selectIds,
-        selectEntities,
-        selectAll,
-        selectTotal,
-        selectById: createSelector(selectEntities, selectId, selectById),
+      const selectors: EntitySelectors<T, any, Id, 's', 'p'> = {
+        [`select${capitalize(single)}Ids` as const]: selectIds,
+        [`select${capitalize(single)}Entities` as const]: selectEntities,
+        [`selectAll${capitalize(plural)}` as const]: selectAll,
+        [`selectTotal${capitalize(plural)}` as const]: selectTotal,
+        [`select${capitalize(single)}ById` as const]: createSelector(
+          selectEntities,
+          selectId,
+          selectById,
+        ),
       }
+      return selectors as any
     }
 
     const selectGlobalizedEntities = createSelector(
@@ -60,17 +95,28 @@ export function createSelectorsFactory<T, Id extends EntityId>() {
       selectEntities,
     )
 
-    return {
-      selectIds: createSelector(selectState, selectIds),
-      selectEntities: selectGlobalizedEntities,
-      selectAll: createSelector(selectState, selectAll),
-      selectTotal: createSelector(selectState, selectTotal),
-      selectById: createSelector(
+    const selectors: EntitySelectors<T, any, Id, 's', 'p'> = {
+      [`select${capitalize(single)}Ids` as const]: createSelector(
+        selectState,
+        selectIds,
+      ),
+      [`select${capitalize(single)}Entities` as const]:
+        selectGlobalizedEntities,
+      [`selectAll${capitalize(plural)}` as const]: createSelector(
+        selectState,
+        selectAll,
+      ),
+      [`selectTotal${capitalize(plural)}` as const]: createSelector(
+        selectState,
+        selectTotal,
+      ),
+      [`select${capitalize(single)}ById` as const]: createSelector(
         selectGlobalizedEntities,
         selectId,
         selectById,
       ),
     }
+    return selectors as any
   }
 
   return { getSelectors }
diff --git a/packages/toolkit/src/entities/tests/state_selectors.test.ts b/packages/toolkit/src/entities/tests/state_selectors.test.ts
index dba56cc01d..ef7a48e9eb 100644
--- a/packages/toolkit/src/entities/tests/state_selectors.test.ts
+++ b/packages/toolkit/src/entities/tests/state_selectors.test.ts
@@ -147,6 +147,61 @@ describe('Entity State Selectors', () => {
       memoizeSpy.mockClear()
     })
   })
+  describe('named selectors', () => {
+    interface State {
+      books: EntityState<BookModel, string>
+    }
+
+    let adapter: EntityAdapter<BookModel, string>
+    let state: State
+
+    beforeEach(() => {
+      adapter = createEntityAdapter({
+        selectId: (book: BookModel) => book.id,
+      })
+
+      state = {
+        books: adapter.setAll(adapter.getInitialState(), [
+          AClockworkOrange,
+          AnimalFarm,
+          TheGreatGatsby,
+        ]),
+      }
+    })
+    it('should use the provided name and pluralName', () => {
+      const selectors = adapter.getSelectors(undefined, {
+        name: 'book',
+      })
+
+      expect(selectors.selectAllBooks).toBeTypeOf('function')
+      expect(selectors.selectTotalBooks).toBeTypeOf('function')
+      expect(selectors.selectBookById).toBeTypeOf('function')
+
+      expect(selectors.selectAllBooks(state.books)).toEqual([
+        AClockworkOrange,
+        AnimalFarm,
+        TheGreatGatsby,
+      ])
+      expect(selectors.selectTotalBooks(state.books)).toEqual(3)
+    })
+    it('should use the plural of the provided name', () => {
+      const selectors = adapter.getSelectors((state: State) => state.books, {
+        name: 'book',
+        pluralName: 'bookies',
+      })
+
+      expect(selectors.selectAllBookies).toBeTypeOf('function')
+      expect(selectors.selectTotalBookies).toBeTypeOf('function')
+      expect(selectors.selectBookById).toBeTypeOf('function')
+
+      expect(selectors.selectAllBookies(state)).toEqual([
+        AClockworkOrange,
+        AnimalFarm,
+        TheGreatGatsby,
+      ])
+      expect(selectors.selectTotalBookies(state)).toEqual(3)
+    })
+  })
 })
 
 function expectType<T>(t: T) {

From 2653a61762923dab2330d88b93a075b27262f144 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Mon, 12 Feb 2024 18:21:21 +0000
Subject: [PATCH 118/178] call with whole action

---
 .../toolkit/src/entities/slice_creator.ts     | 22 +++++++++----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/packages/toolkit/src/entities/slice_creator.ts b/packages/toolkit/src/entities/slice_creator.ts
index 179eeff07f..b50ee26d10 100644
--- a/packages/toolkit/src/entities/slice_creator.ts
+++ b/packages/toolkit/src/entities/slice_creator.ts
@@ -100,54 +100,54 @@ export function createEntityMethods<
   const reducer = reducerCreator.create
   const reducers: EntityReducers<T, Id, State, 's', 'p'> = {
     [`addOne${capitalize(name)}` as const]: reducer<T>((state, action) => {
-      adapter.addOne(selectEntityState(state), action.payload)
+      adapter.addOne(selectEntityState(state), action)
     }),
     [`addMany${capitalize(pluralName)}` as const]: reducer<
       readonly T[] | Record<Id, T>
     >((state, action) => {
-      adapter.addMany(selectEntityState(state), action.payload)
+      adapter.addMany(selectEntityState(state), action)
     }),
     [`setOne${capitalize(name)}` as const]: reducer<T>((state, action) => {
-      adapter.setOne(selectEntityState(state), action.payload)
+      adapter.setOne(selectEntityState(state), action)
     }),
     [`setMany${capitalize(pluralName)}` as const]: reducer<
       readonly T[] | Record<Id, T>
     >((state, action) => {
-      adapter.setMany(selectEntityState(state), action.payload)
+      adapter.setMany(selectEntityState(state), action)
     }),
     [`setAll${capitalize(pluralName)}` as const]: reducer<
       readonly T[] | Record<Id, T>
     >((state, action) => {
-      adapter.setAll(selectEntityState(state), action.payload)
+      adapter.setAll(selectEntityState(state), action)
     }),
     [`removeOne${capitalize(name)}` as const]: reducer<Id>((state, action) => {
-      adapter.removeOne(selectEntityState(state), action.payload)
+      adapter.removeOne(selectEntityState(state), action)
     }),
     [`removeMany${capitalize(pluralName)}` as const]: reducer<readonly Id[]>(
       (state, action) => {
-        adapter.removeMany(selectEntityState(state), action.payload)
+        adapter.removeMany(selectEntityState(state), action)
       },
     ),
     [`removeAll${capitalize(pluralName)}` as const]: reducer((state) => {
       adapter.removeAll(selectEntityState(state))
     }),
     [`upsertOne${capitalize(name)}` as const]: reducer<T>((state, action) => {
-      adapter.upsertOne(selectEntityState(state), action.payload)
+      adapter.upsertOne(selectEntityState(state), action)
     }),
     [`upsertMany${capitalize(pluralName)}` as const]: reducer<
       readonly T[] | Record<Id, T>
     >((state, action) => {
-      adapter.upsertMany(selectEntityState(state), action.payload)
+      adapter.upsertMany(selectEntityState(state), action)
     }),
     [`updateOne${capitalize(name)}` as const]: reducer<Update<T, Id>>(
       (state, action) => {
-        adapter.updateOne(selectEntityState(state), action.payload)
+        adapter.updateOne(selectEntityState(state), action)
       },
     ),
     [`updateMany${capitalize(pluralName)}` as const]: reducer<
       readonly Update<T, Id>[]
     >((state, action) => {
-      adapter.updateMany(selectEntityState(state), action.payload)
+      adapter.updateMany(selectEntityState(state), action)
     }),
   }
   return reducers as any

From 29d70bbaea8c00fe489a1a879dc8aeb4d4a1eb28 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Tue, 13 Feb 2024 21:59:51 +0000
Subject: [PATCH 119/178] ensure selectors are computed

---
 packages/toolkit/src/entities/models.ts | 30 +++++++++++++------------
 1 file changed, 16 insertions(+), 14 deletions(-)

diff --git a/packages/toolkit/src/entities/models.ts b/packages/toolkit/src/entities/models.ts
index b39d679eba..d1b17cf28e 100644
--- a/packages/toolkit/src/entities/models.ts
+++ b/packages/toolkit/src/entities/models.ts
@@ -166,20 +166,22 @@ export type EntitySelectors<
   Id extends EntityId,
   Single extends string = '',
   Plural extends string = DefaultPlural<Single>,
-> = {
-  [K in `select${Capitalize<Single>}Ids`]: (state: V) => Id[]
-} & {
-  [K in `select${Capitalize<Single>}Entities`]: (state: V) => Record<Id, T>
-} & {
-  [K in `selectAll${Capitalize<Plural>}`]: (state: V) => T[]
-} & {
-  [K in `selectTotal${Capitalize<Plural>}`]: (state: V) => number
-} & {
-  [K in `select${Capitalize<Single>}ById`]: (
-    state: V,
-    id: Id,
-  ) => Compute<UncheckedIndexedAccess<T>>
-}
+> = Compute<
+  {
+    [K in `select${Capitalize<Single>}Ids`]: (state: V) => Id[]
+  } & {
+    [K in `select${Capitalize<Single>}Entities`]: (state: V) => Record<Id, T>
+  } & {
+    [K in `selectAll${Capitalize<Plural>}`]: (state: V) => T[]
+  } & {
+    [K in `selectTotal${Capitalize<Plural>}`]: (state: V) => number
+  } & {
+    [K in `select${Capitalize<Single>}ById`]: (
+      state: V,
+      id: Id,
+    ) => Compute<UncheckedIndexedAccess<T>>
+  }
+>
 
 export type DefaultPlural<Single extends string> = Single extends ''
   ? ''

From 3d2ecf66923ce70f46f2808fcfc2bc6272b71025 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.durrant@marketdojo.com>
Date: Thu, 15 Feb 2024 11:10:33 +0000
Subject: [PATCH 120/178] remove unused PURE

---
 packages/toolkit/src/createSlice.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index b11948a5b5..e09a111dcf 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -874,7 +874,7 @@ export const reducerCreator: ReducerCreator<ReducerType.reducer> = {
 }
 
 export const preparedReducerCreator: ReducerCreator<ReducerType.reducerWithPrepare> =
-  /*@__PURE__*/ {
+  {
     type: ReducerType.reducerWithPrepare,
     create(prepare, reducer) {
       return {

From 46e792b4e1031fbc6c03b740f2af1922a86cf84e Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sun, 18 Feb 2024 21:41:05 +0000
Subject: [PATCH 121/178] byte shaving

---
 packages/toolkit/src/createSlice.ts | 18 +++++-------------
 1 file changed, 5 insertions(+), 13 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index e09a111dcf..3de4098133 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -1119,19 +1119,11 @@ export function buildCreateSlice<
           reducerName,
           type: getType(name, reducerName),
         }
-        if ('reducer' in reducerDefinition) {
-          preparedReducerCreator.handle(
-            reducerDetails,
-            reducerDefinition as any,
-            contextMethods,
-          )
-        } else {
-          reducerCreator.handle(
-            reducerDetails,
-            reducerDefinition as any,
-            contextMethods,
-          )
-        }
+        const handler =
+          'reduce' in reducerDefinition
+            ? preparedReducerCreator
+            : reducerCreator
+        handler.handle(reducerDetails, reducerDefinition as any, contextMethods)
       }
     }
 

From 930e868aee9cb87f4f17a0d84978ab3e63e96277 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sun, 18 Feb 2024 21:45:12 +0000
Subject: [PATCH 122/178] expose sliceName to reducer handler

---
 packages/toolkit/src/createSlice.ts | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 3de4098133..2aeb2ee61b 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -276,9 +276,11 @@ export interface ReducerHandlingContextMethods<State> {
 }
 
 export interface ReducerDetails {
+  /** The name of the slice */
+  sliceName: string
   /** The key the reducer was defined under */
   reducerName: string
-  /** The predefined action type, i.e. `${slice.name}/${reducerName}` */
+  /** The predefined action type, i.e. `${sliceName}/${reducerName}` */
   type: string
 }
 
@@ -1106,6 +1108,7 @@ export function buildCreateSlice<
           throw new Error(`Unsupported reducer type: ${String(type)}`)
         }
         const reducerDetails: ReducerDetails = {
+          sliceName: name,
           reducerName,
           type: getType(name, reducerName),
         }
@@ -1116,6 +1119,7 @@ export function buildCreateSlice<
         options.reducers as SliceCaseReducers<State>,
       )) {
         const reducerDetails: ReducerDetails = {
+          sliceName: name,
           reducerName,
           type: getType(name, reducerName),
         }

From ece8442f428053f47121626cab344b61661fa389 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sun, 18 Feb 2024 21:58:35 +0000
Subject: [PATCH 123/178] split asyncThunkCreator into its own file

---
 packages/toolkit/src/asyncThunkCreator.ts     | 208 ++++++++++++++
 packages/toolkit/src/createSlice.ts           | 196 +------------
 packages/toolkit/src/index.ts                 |   4 +-
 .../src/tests/asyncThunkCreator.test.ts       | 265 ++++++++++++++++++
 .../toolkit/src/tests/createSlice.test.ts     | 256 -----------------
 5 files changed, 476 insertions(+), 453 deletions(-)
 create mode 100644 packages/toolkit/src/asyncThunkCreator.ts
 create mode 100644 packages/toolkit/src/tests/asyncThunkCreator.test.ts

diff --git a/packages/toolkit/src/asyncThunkCreator.ts b/packages/toolkit/src/asyncThunkCreator.ts
new file mode 100644
index 0000000000..4f596ac4b9
--- /dev/null
+++ b/packages/toolkit/src/asyncThunkCreator.ts
@@ -0,0 +1,208 @@
+import type {
+  AsyncThunk,
+  AsyncThunkConfig,
+  AsyncThunkOptions,
+  AsyncThunkPayloadCreator,
+  OverrideThunkApiConfigs,
+} from './createAsyncThunk'
+import { createAsyncThunk } from './createAsyncThunk'
+import type { CaseReducer } from './createReducer'
+import type {
+  CreatorCaseReducers,
+  ReducerCreator,
+  ReducerCreatorEntry,
+  ReducerDefinition,
+  ReducerNamesOfType,
+} from './createSlice'
+import { ReducerType } from './createSlice'
+import type { Id } from './tsHelpers'
+
+declare module '@reduxjs/toolkit' {
+  export interface SliceReducerCreators<
+    State,
+    CaseReducers extends CreatorCaseReducers<State>,
+    Name extends string = string,
+  > {
+    [ReducerType.asyncThunk]: ReducerCreatorEntry<
+      AsyncThunkCreator<State>,
+      {
+        actions: {
+          [ReducerName in ReducerNamesOfType<
+            CaseReducers,
+            ReducerType.asyncThunk
+          >]: CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition<
+            State,
+            infer ThunkArg,
+            infer Returned,
+            infer ThunkApiConfig
+          >
+            ? AsyncThunk<Returned, ThunkArg, ThunkApiConfig>
+            : never
+        }
+        caseReducers: {
+          [ReducerName in ReducerNamesOfType<
+            CaseReducers,
+            ReducerType.asyncThunk
+          >]: CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition<
+            State,
+            any,
+            any,
+            any
+          >
+            ? Id<
+                Pick<
+                  Required<CaseReducers[ReducerName]>,
+                  'fulfilled' | 'rejected' | 'pending' | 'settled'
+                >
+              >
+            : never
+        }
+      }
+    >
+  }
+}
+
+export interface AsyncThunkSliceReducerConfig<
+  State,
+  ThunkArg extends any,
+  Returned = unknown,
+  ThunkApiConfig extends AsyncThunkConfig = {},
+> {
+  pending?: CaseReducer<
+    State,
+    ReturnType<AsyncThunk<Returned, ThunkArg, ThunkApiConfig>['pending']>
+  >
+  rejected?: CaseReducer<
+    State,
+    ReturnType<AsyncThunk<Returned, ThunkArg, ThunkApiConfig>['rejected']>
+  >
+  fulfilled?: CaseReducer<
+    State,
+    ReturnType<AsyncThunk<Returned, ThunkArg, ThunkApiConfig>['fulfilled']>
+  >
+  settled?: CaseReducer<
+    State,
+    ReturnType<
+      AsyncThunk<Returned, ThunkArg, ThunkApiConfig>['rejected' | 'fulfilled']
+    >
+  >
+  options?: AsyncThunkOptions<ThunkArg, ThunkApiConfig>
+}
+
+export interface AsyncThunkSliceReducerDefinition<
+  State,
+  ThunkArg extends any,
+  Returned = unknown,
+  ThunkApiConfig extends AsyncThunkConfig = {},
+> extends AsyncThunkSliceReducerConfig<
+      State,
+      ThunkArg,
+      Returned,
+      ThunkApiConfig
+    >,
+    ReducerDefinition<ReducerType.asyncThunk> {
+  payloadCreator: AsyncThunkPayloadCreator<Returned, ThunkArg, ThunkApiConfig>
+}
+
+/**
+ * Providing these as part of the config would cause circular types, so we disallow passing them
+ */
+type PreventCircular<ThunkApiConfig> = {
+  [K in keyof ThunkApiConfig]: K extends 'state' | 'dispatch'
+    ? never
+    : ThunkApiConfig[K]
+}
+
+interface AsyncThunkCreator<
+  State,
+  CurriedThunkApiConfig extends
+    PreventCircular<AsyncThunkConfig> = PreventCircular<AsyncThunkConfig>,
+> {
+  <Returned, ThunkArg = void>(
+    payloadCreator: AsyncThunkPayloadCreator<
+      Returned,
+      ThunkArg,
+      CurriedThunkApiConfig
+    >,
+    config?: AsyncThunkSliceReducerConfig<
+      State,
+      ThunkArg,
+      Returned,
+      CurriedThunkApiConfig
+    >,
+  ): AsyncThunkSliceReducerDefinition<
+    State,
+    ThunkArg,
+    Returned,
+    CurriedThunkApiConfig
+  >
+  <
+    Returned,
+    ThunkArg,
+    ThunkApiConfig extends PreventCircular<AsyncThunkConfig> = {},
+  >(
+    payloadCreator: AsyncThunkPayloadCreator<
+      Returned,
+      ThunkArg,
+      ThunkApiConfig
+    >,
+    config?: AsyncThunkSliceReducerConfig<
+      State,
+      ThunkArg,
+      Returned,
+      ThunkApiConfig
+    >,
+  ): AsyncThunkSliceReducerDefinition<State, ThunkArg, Returned, ThunkApiConfig>
+  withTypes<
+    ThunkApiConfig extends PreventCircular<AsyncThunkConfig>,
+  >(): AsyncThunkCreator<
+    State,
+    OverrideThunkApiConfigs<CurriedThunkApiConfig, ThunkApiConfig>
+  >
+}
+
+export const asyncThunkCreator: ReducerCreator<ReducerType.asyncThunk> = {
+  type: ReducerType.asyncThunk,
+  create: /* @__PURE__ */ (() => {
+    function asyncThunk(
+      payloadCreator: AsyncThunkPayloadCreator<any, any>,
+      config: AsyncThunkSliceReducerConfig<any, any>,
+    ): AsyncThunkSliceReducerDefinition<any, any> {
+      return {
+        _reducerDefinitionType: ReducerType.asyncThunk,
+        payloadCreator,
+        ...config,
+      }
+    }
+    asyncThunk.withTypes = () => asyncThunk
+    return asyncThunk as AsyncThunkCreator<any>
+  })(),
+  handle({ type, reducerName }, definition, context) {
+    const { payloadCreator, fulfilled, pending, rejected, settled, options } =
+      definition
+    const thunk = createAsyncThunk(type, payloadCreator, options as any)
+    context.exposeAction(reducerName, thunk)
+
+    if (fulfilled) {
+      context.addCase(thunk.fulfilled, fulfilled)
+    }
+    if (pending) {
+      context.addCase(thunk.pending, pending)
+    }
+    if (rejected) {
+      context.addCase(thunk.rejected, rejected)
+    }
+    if (settled) {
+      context.addMatcher(thunk.settled, settled)
+    }
+
+    context.exposeCaseReducer(reducerName, {
+      fulfilled: fulfilled || noop,
+      pending: pending || noop,
+      rejected: rejected || noop,
+      settled: settled || noop,
+    })
+  },
+}
+
+function noop() {}
diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 2aeb2ee61b..440ed664d7 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -16,21 +16,8 @@ import type {
 import { createReducer, makeGetInitialState } from './createReducer'
 import type { ActionReducerMapBuilder, TypedActionCreator } from './mapBuilders'
 import { executeReducerBuilderCallback } from './mapBuilders'
-import type {
-  Id,
-  IfMaybeUndefined,
-  TypeGuard,
-  UnionToIntersection,
-} from './tsHelpers'
+import type { Id, TypeGuard, UnionToIntersection } from './tsHelpers'
 import type { InjectConfig } from './combineSlices'
-import type {
-  AsyncThunk,
-  AsyncThunkConfig,
-  AsyncThunkOptions,
-  AsyncThunkPayloadCreator,
-  OverrideThunkApiConfigs,
-} from './createAsyncThunk'
-import { createAsyncThunk } from './createAsyncThunk'
 import { emplace } from './utils'
 
 export enum ReducerType {
@@ -137,42 +124,6 @@ export interface SliceReducerCreators<
       }
     }
   >
-  [ReducerType.asyncThunk]: ReducerCreatorEntry<
-    AsyncThunkCreator<State>,
-    {
-      actions: {
-        [ReducerName in ReducerNamesOfType<
-          CaseReducers,
-          ReducerType.asyncThunk
-        >]: CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition<
-          State,
-          infer ThunkArg,
-          infer Returned,
-          infer ThunkApiConfig
-        >
-          ? AsyncThunk<Returned, ThunkArg, ThunkApiConfig>
-          : never
-      }
-      caseReducers: {
-        [ReducerName in ReducerNamesOfType<
-          CaseReducers,
-          ReducerType.asyncThunk
-        >]: CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition<
-          State,
-          any,
-          any,
-          any
-        >
-          ? Id<
-              Pick<
-                Required<CaseReducers[ReducerName]>,
-                'fulfilled' | 'rejected' | 'pending' | 'settled'
-              >
-            >
-          : never
-      }
-    }
-  >
 }
 
 export type ReducerCreators<
@@ -616,105 +567,6 @@ export interface PreparedCaseReducerDefinition<
   >
 }
 
-export interface AsyncThunkSliceReducerConfig<
-  State,
-  ThunkArg extends any,
-  Returned = unknown,
-  ThunkApiConfig extends AsyncThunkConfig = {},
-> {
-  pending?: CaseReducer<
-    State,
-    ReturnType<AsyncThunk<Returned, ThunkArg, ThunkApiConfig>['pending']>
-  >
-  rejected?: CaseReducer<
-    State,
-    ReturnType<AsyncThunk<Returned, ThunkArg, ThunkApiConfig>['rejected']>
-  >
-  fulfilled?: CaseReducer<
-    State,
-    ReturnType<AsyncThunk<Returned, ThunkArg, ThunkApiConfig>['fulfilled']>
-  >
-  settled?: CaseReducer<
-    State,
-    ReturnType<
-      AsyncThunk<Returned, ThunkArg, ThunkApiConfig>['rejected' | 'fulfilled']
-    >
-  >
-  options?: AsyncThunkOptions<ThunkArg, ThunkApiConfig>
-}
-
-export interface AsyncThunkSliceReducerDefinition<
-  State,
-  ThunkArg extends any,
-  Returned = unknown,
-  ThunkApiConfig extends AsyncThunkConfig = {},
-> extends AsyncThunkSliceReducerConfig<
-      State,
-      ThunkArg,
-      Returned,
-      ThunkApiConfig
-    >,
-    ReducerDefinition<ReducerType.asyncThunk> {
-  payloadCreator: AsyncThunkPayloadCreator<Returned, ThunkArg, ThunkApiConfig>
-}
-
-/**
- * Providing these as part of the config would cause circular types, so we disallow passing them
- */
-type PreventCircular<ThunkApiConfig> = {
-  [K in keyof ThunkApiConfig]: K extends 'state' | 'dispatch'
-    ? never
-    : ThunkApiConfig[K]
-}
-
-interface AsyncThunkCreator<
-  State,
-  CurriedThunkApiConfig extends
-    PreventCircular<AsyncThunkConfig> = PreventCircular<AsyncThunkConfig>,
-> {
-  <Returned, ThunkArg = void>(
-    payloadCreator: AsyncThunkPayloadCreator<
-      Returned,
-      ThunkArg,
-      CurriedThunkApiConfig
-    >,
-    config?: AsyncThunkSliceReducerConfig<
-      State,
-      ThunkArg,
-      Returned,
-      CurriedThunkApiConfig
-    >,
-  ): AsyncThunkSliceReducerDefinition<
-    State,
-    ThunkArg,
-    Returned,
-    CurriedThunkApiConfig
-  >
-  <
-    Returned,
-    ThunkArg,
-    ThunkApiConfig extends PreventCircular<AsyncThunkConfig> = {},
-  >(
-    payloadCreator: AsyncThunkPayloadCreator<
-      Returned,
-      ThunkArg,
-      ThunkApiConfig
-    >,
-    config?: AsyncThunkSliceReducerConfig<
-      State,
-      ThunkArg,
-      Returned,
-      ThunkApiConfig
-    >,
-  ): AsyncThunkSliceReducerDefinition<State, ThunkArg, Returned, ThunkApiConfig>
-  withTypes<
-    ThunkApiConfig extends PreventCircular<AsyncThunkConfig>,
-  >(): AsyncThunkCreator<
-    State,
-    OverrideThunkApiConfigs<CurriedThunkApiConfig, ThunkApiConfig>
-  >
-}
-
 /**
  * The type describing a slice's `reducers` option.
  *
@@ -893,52 +745,6 @@ export const preparedReducerCreator: ReducerCreator<ReducerType.reducerWithPrepa
     },
   }
 
-export const asyncThunkCreator: ReducerCreator<ReducerType.asyncThunk> = {
-  type: ReducerType.asyncThunk,
-  create: /* @__PURE__ */ (() => {
-    function asyncThunk(
-      payloadCreator: AsyncThunkPayloadCreator<any, any>,
-      config: AsyncThunkSliceReducerConfig<any, any>,
-    ): AsyncThunkSliceReducerDefinition<any, any> {
-      return {
-        _reducerDefinitionType: ReducerType.asyncThunk,
-        payloadCreator,
-        ...config,
-      }
-    }
-    asyncThunk.withTypes = () => asyncThunk
-    return asyncThunk as AsyncThunkCreator<any>
-  })(),
-  handle({ type, reducerName }, definition, context) {
-    const { payloadCreator, fulfilled, pending, rejected, settled, options } =
-      definition
-    const thunk = createAsyncThunk(type, payloadCreator, options as any)
-    context.exposeAction(reducerName, thunk)
-
-    if (fulfilled) {
-      context.addCase(thunk.fulfilled, fulfilled)
-    }
-    if (pending) {
-      context.addCase(thunk.pending, pending)
-    }
-    if (rejected) {
-      context.addCase(thunk.rejected, rejected)
-    }
-    if (settled) {
-      context.addMatcher(thunk.settled, settled)
-    }
-
-    context.exposeCaseReducer(reducerName, {
-      fulfilled: fulfilled || noop,
-      pending: pending || noop,
-      rejected: rejected || noop,
-      settled: settled || noop,
-    })
-  },
-}
-
-function noop() {}
-
 const isCreatorCallback = <
   State,
   CreatorMap extends Record<string, RegisteredReducerType>,
diff --git a/packages/toolkit/src/index.ts b/packages/toolkit/src/index.ts
index 5e96705ce7..51e044a433 100644
--- a/packages/toolkit/src/index.ts
+++ b/packages/toolkit/src/index.ts
@@ -67,9 +67,9 @@ export {
   buildCreateSlice,
   reducerCreator,
   preparedReducerCreator,
-  asyncThunkCreator,
   ReducerType,
 } from './createSlice'
+export { asyncThunkCreator } from './asyncThunkCreator'
 
 export type {
   // types
@@ -92,8 +92,8 @@ export type {
   SliceActionType,
   CaseReducerDefinition,
   PreparedCaseReducerDefinition,
-  AsyncThunkSliceReducerDefinition,
 } from './createSlice'
+export type { AsyncThunkSliceReducerDefinition } from './asyncThunkCreator'
 export type { ActionCreatorInvariantMiddlewareOptions } from './actionCreatorInvariantMiddleware'
 export { createActionCreatorInvariantMiddleware } from './actionCreatorInvariantMiddleware'
 export {
diff --git a/packages/toolkit/src/tests/asyncThunkCreator.test.ts b/packages/toolkit/src/tests/asyncThunkCreator.test.ts
new file mode 100644
index 0000000000..13b5756585
--- /dev/null
+++ b/packages/toolkit/src/tests/asyncThunkCreator.test.ts
@@ -0,0 +1,265 @@
+import {
+  asyncThunkCreator,
+  buildCreateSlice,
+  createSlice,
+  configureStore,
+} from '@reduxjs/toolkit'
+
+describe('asyncThunkCreator', () => {
+  describe('reducers definition with asyncThunks', () => {
+    it('is disabled by default', () => {
+      expect(() =>
+        createSlice({
+          name: 'test',
+          initialState: [] as any[],
+          // @ts-expect-error asyncThunk not in creators
+          reducers: (create) => ({ thunk: create.asyncThunk(() => {}) }),
+        }),
+      ).toThrowErrorMatchingInlineSnapshot(
+        `[TypeError: create.asyncThunk is not a function]`,
+      )
+    })
+    const createAppSlice = buildCreateSlice({
+      creators: { asyncThunk: asyncThunkCreator },
+    })
+    function pending(state: any[], action: any) {
+      state.push(['pendingReducer', action])
+    }
+    function fulfilled(state: any[], action: any) {
+      state.push(['fulfilledReducer', action])
+    }
+    function rejected(state: any[], action: any) {
+      state.push(['rejectedReducer', action])
+    }
+    function settled(state: any[], action: any) {
+      state.push(['settledReducer', action])
+    }
+
+    test('successful thunk', async () => {
+      const slice = createAppSlice({
+        name: 'test',
+        initialState: [] as any[],
+        reducers: (create) => ({
+          thunkReducers: create.asyncThunk(
+            function payloadCreator(arg, api) {
+              return Promise.resolve('resolved payload')
+            },
+            { pending, fulfilled, rejected, settled },
+          ),
+        }),
+      })
+
+      const store = configureStore({
+        reducer: slice.reducer,
+      })
+      await store.dispatch(slice.actions.thunkReducers())
+      expect(store.getState()).toMatchObject([
+        [
+          'pendingReducer',
+          {
+            type: 'test/thunkReducers/pending',
+            payload: undefined,
+          },
+        ],
+        [
+          'fulfilledReducer',
+          {
+            type: 'test/thunkReducers/fulfilled',
+            payload: 'resolved payload',
+          },
+        ],
+        [
+          'settledReducer',
+          {
+            type: 'test/thunkReducers/fulfilled',
+            payload: 'resolved payload',
+          },
+        ],
+      ])
+    })
+
+    test('rejected thunk', async () => {
+      const slice = createAppSlice({
+        name: 'test',
+        initialState: [] as any[],
+        reducers: (create) => ({
+          thunkReducers: create.asyncThunk(
+            // payloadCreator isn't allowed to return never
+            function payloadCreator(arg, api): any {
+              throw new Error('')
+            },
+            { pending, fulfilled, rejected, settled },
+          ),
+        }),
+      })
+
+      const store = configureStore({
+        reducer: slice.reducer,
+      })
+      await store.dispatch(slice.actions.thunkReducers())
+      expect(store.getState()).toMatchObject([
+        [
+          'pendingReducer',
+          {
+            type: 'test/thunkReducers/pending',
+            payload: undefined,
+          },
+        ],
+        [
+          'rejectedReducer',
+          {
+            type: 'test/thunkReducers/rejected',
+            payload: undefined,
+          },
+        ],
+        [
+          'settledReducer',
+          {
+            type: 'test/thunkReducers/rejected',
+            payload: undefined,
+          },
+        ],
+      ])
+    })
+
+    test('with options', async () => {
+      const slice = createAppSlice({
+        name: 'test',
+        initialState: [] as any[],
+        reducers: (create) => ({
+          thunkReducers: create.asyncThunk(
+            function payloadCreator(arg, api) {
+              return 'should not call this'
+            },
+            {
+              options: {
+                condition() {
+                  return false
+                },
+                dispatchConditionRejection: true,
+              },
+              pending,
+              fulfilled,
+              rejected,
+              settled,
+            },
+          ),
+        }),
+      })
+
+      const store = configureStore({
+        reducer: slice.reducer,
+      })
+      await store.dispatch(slice.actions.thunkReducers())
+      expect(store.getState()).toMatchObject([
+        [
+          'rejectedReducer',
+          {
+            type: 'test/thunkReducers/rejected',
+            payload: undefined,
+            meta: { condition: true },
+          },
+        ],
+        [
+          'settledReducer',
+          {
+            type: 'test/thunkReducers/rejected',
+            payload: undefined,
+            meta: { condition: true },
+          },
+        ],
+      ])
+    })
+
+    test('has caseReducers for the asyncThunk', async () => {
+      const slice = createAppSlice({
+        name: 'test',
+        initialState: [],
+        reducers: (create) => ({
+          thunkReducers: create.asyncThunk(
+            function payloadCreator(arg, api) {
+              return Promise.resolve('resolved payload')
+            },
+            { pending, fulfilled, settled },
+          ),
+        }),
+      })
+
+      expect(slice.caseReducers.thunkReducers.pending).toBe(pending)
+      expect(slice.caseReducers.thunkReducers.fulfilled).toBe(fulfilled)
+      expect(slice.caseReducers.thunkReducers.settled).toBe(settled)
+      // even though it is not defined above, this should at least be a no-op function to match the TypeScript typings
+      // and should be callable as a reducer even if it does nothing
+      expect(() =>
+        slice.caseReducers.thunkReducers.rejected(
+          [],
+          slice.actions.thunkReducers.rejected(
+            new Error('test'),
+            'fakeRequestId',
+          ),
+        ),
+      ).not.toThrow()
+    })
+
+    test('can define reducer with prepare statement using create.preparedReducer', async () => {
+      const slice = createSlice({
+        name: 'test',
+        initialState: [] as any[],
+        reducers: (create) => ({
+          prepared: create.preparedReducer(
+            (p: string, m: number, e: { message: string }) => ({
+              payload: p,
+              meta: m,
+              error: e,
+            }),
+            (state, action) => {
+              state.push(action)
+            },
+          ),
+        }),
+      })
+
+      expect(
+        slice.reducer(
+          [],
+          slice.actions.prepared('test', 1, { message: 'err' }),
+        ),
+      ).toMatchInlineSnapshot(`
+        [
+          {
+            "error": {
+              "message": "err",
+            },
+            "meta": 1,
+            "payload": "test",
+            "type": "test/prepared",
+          },
+        ]
+      `)
+    })
+
+    test('throws an error when invoked with a normal `prepare` object that has not gone through a `create.preparedReducer` call', async () => {
+      expect(() =>
+        createSlice({
+          name: 'test',
+          initialState: [] as any[],
+          // @ts-expect-error
+          reducers: (create) => ({
+            prepared: {
+              prepare: (p: string, m: number, e: { message: string }) => ({
+                payload: p,
+                meta: m,
+                error: e,
+              }),
+              reducer: (state: any[], action: any) => {
+                state.push(action)
+              },
+            },
+          }),
+        }),
+      ).toThrowErrorMatchingInlineSnapshot(
+        `[Error: Please use reducer creators passed to callback. Each reducer definition must have a \`_reducerDefinitionType\` property indicating which handler to use.]`,
+      )
+    })
+  })
+})
diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index 95f2462eb1..e2a20e8d3c 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -673,262 +673,6 @@ describe('createSlice', () => {
       expect(injected.selectors).not.toBe(injected2.selectors)
     })
   })
-  describe('reducers definition with asyncThunks', () => {
-    it('is disabled by default', () => {
-      expect(() =>
-        createSlice({
-          name: 'test',
-          initialState: [] as any[],
-          // @ts-expect-error asyncThunk not in creators
-          reducers: (create) => ({ thunk: create.asyncThunk(() => {}) }),
-        }),
-      ).toThrowErrorMatchingInlineSnapshot(
-        `[TypeError: create.asyncThunk is not a function]`,
-      )
-    })
-    const createAppSlice = buildCreateSlice({
-      creators: { asyncThunk: asyncThunkCreator },
-    })
-    function pending(state: any[], action: any) {
-      state.push(['pendingReducer', action])
-    }
-    function fulfilled(state: any[], action: any) {
-      state.push(['fulfilledReducer', action])
-    }
-    function rejected(state: any[], action: any) {
-      state.push(['rejectedReducer', action])
-    }
-    function settled(state: any[], action: any) {
-      state.push(['settledReducer', action])
-    }
-
-    test('successful thunk', async () => {
-      const slice = createAppSlice({
-        name: 'test',
-        initialState: [] as any[],
-        reducers: (create) => ({
-          thunkReducers: create.asyncThunk(
-            function payloadCreator(arg, api) {
-              return Promise.resolve('resolved payload')
-            },
-            { pending, fulfilled, rejected, settled },
-          ),
-        }),
-      })
-
-      const store = configureStore({
-        reducer: slice.reducer,
-      })
-      await store.dispatch(slice.actions.thunkReducers())
-      expect(store.getState()).toMatchObject([
-        [
-          'pendingReducer',
-          {
-            type: 'test/thunkReducers/pending',
-            payload: undefined,
-          },
-        ],
-        [
-          'fulfilledReducer',
-          {
-            type: 'test/thunkReducers/fulfilled',
-            payload: 'resolved payload',
-          },
-        ],
-        [
-          'settledReducer',
-          {
-            type: 'test/thunkReducers/fulfilled',
-            payload: 'resolved payload',
-          },
-        ],
-      ])
-    })
-
-    test('rejected thunk', async () => {
-      const slice = createAppSlice({
-        name: 'test',
-        initialState: [] as any[],
-        reducers: (create) => ({
-          thunkReducers: create.asyncThunk(
-            // payloadCreator isn't allowed to return never
-            function payloadCreator(arg, api): any {
-              throw new Error('')
-            },
-            { pending, fulfilled, rejected, settled },
-          ),
-        }),
-      })
-
-      const store = configureStore({
-        reducer: slice.reducer,
-      })
-      await store.dispatch(slice.actions.thunkReducers())
-      expect(store.getState()).toMatchObject([
-        [
-          'pendingReducer',
-          {
-            type: 'test/thunkReducers/pending',
-            payload: undefined,
-          },
-        ],
-        [
-          'rejectedReducer',
-          {
-            type: 'test/thunkReducers/rejected',
-            payload: undefined,
-          },
-        ],
-        [
-          'settledReducer',
-          {
-            type: 'test/thunkReducers/rejected',
-            payload: undefined,
-          },
-        ],
-      ])
-    })
-
-    test('with options', async () => {
-      const slice = createAppSlice({
-        name: 'test',
-        initialState: [] as any[],
-        reducers: (create) => ({
-          thunkReducers: create.asyncThunk(
-            function payloadCreator(arg, api) {
-              return 'should not call this'
-            },
-            {
-              options: {
-                condition() {
-                  return false
-                },
-                dispatchConditionRejection: true,
-              },
-              pending,
-              fulfilled,
-              rejected,
-              settled,
-            },
-          ),
-        }),
-      })
-
-      const store = configureStore({
-        reducer: slice.reducer,
-      })
-      await store.dispatch(slice.actions.thunkReducers())
-      expect(store.getState()).toMatchObject([
-        [
-          'rejectedReducer',
-          {
-            type: 'test/thunkReducers/rejected',
-            payload: undefined,
-            meta: { condition: true },
-          },
-        ],
-        [
-          'settledReducer',
-          {
-            type: 'test/thunkReducers/rejected',
-            payload: undefined,
-            meta: { condition: true },
-          },
-        ],
-      ])
-    })
-
-    test('has caseReducers for the asyncThunk', async () => {
-      const slice = createAppSlice({
-        name: 'test',
-        initialState: [],
-        reducers: (create) => ({
-          thunkReducers: create.asyncThunk(
-            function payloadCreator(arg, api) {
-              return Promise.resolve('resolved payload')
-            },
-            { pending, fulfilled, settled },
-          ),
-        }),
-      })
-
-      expect(slice.caseReducers.thunkReducers.pending).toBe(pending)
-      expect(slice.caseReducers.thunkReducers.fulfilled).toBe(fulfilled)
-      expect(slice.caseReducers.thunkReducers.settled).toBe(settled)
-      // even though it is not defined above, this should at least be a no-op function to match the TypeScript typings
-      // and should be callable as a reducer even if it does nothing
-      expect(() =>
-        slice.caseReducers.thunkReducers.rejected(
-          [],
-          slice.actions.thunkReducers.rejected(
-            new Error('test'),
-            'fakeRequestId',
-          ),
-        ),
-      ).not.toThrow()
-    })
-
-    test('can define reducer with prepare statement using create.preparedReducer', async () => {
-      const slice = createSlice({
-        name: 'test',
-        initialState: [] as any[],
-        reducers: (create) => ({
-          prepared: create.preparedReducer(
-            (p: string, m: number, e: { message: string }) => ({
-              payload: p,
-              meta: m,
-              error: e,
-            }),
-            (state, action) => {
-              state.push(action)
-            },
-          ),
-        }),
-      })
-
-      expect(
-        slice.reducer(
-          [],
-          slice.actions.prepared('test', 1, { message: 'err' }),
-        ),
-      ).toMatchInlineSnapshot(`
-        [
-          {
-            "error": {
-              "message": "err",
-            },
-            "meta": 1,
-            "payload": "test",
-            "type": "test/prepared",
-          },
-        ]
-      `)
-    })
-
-    test('throws an error when invoked with a normal `prepare` object that has not gone through a `create.preparedReducer` call', async () => {
-      expect(() =>
-        createSlice({
-          name: 'test',
-          initialState: [] as any[],
-          // @ts-expect-error
-          reducers: (create) => ({
-            prepared: {
-              prepare: (p: string, m: number, e: { message: string }) => ({
-                payload: p,
-                meta: m,
-                error: e,
-              }),
-              reducer: (state: any[], action: any) => {
-                state.push(action)
-              },
-            },
-          }),
-        }),
-      ).toThrowErrorMatchingInlineSnapshot(
-        `[Error: Please use reducer creators passed to callback. Each reducer definition must have a \`_reducerDefinitionType\` property indicating which handler to use.]`,
-      )
-    })
-  })
   test('reducer and preparedReducer creators can be invoked for object syntax', () => {
     const counterSlice = createSlice({
       name: 'counter',

From 4af6bd45fd517a76c0e9b5620629b3a8e3254284 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sun, 18 Feb 2024 22:00:04 +0000
Subject: [PATCH 124/178] fix typo

---
 packages/toolkit/src/createSlice.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 440ed664d7..524dd8be31 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -930,7 +930,7 @@ export function buildCreateSlice<
           type: getType(name, reducerName),
         }
         const handler =
-          'reduce' in reducerDefinition
+          'reducer' in reducerDefinition
             ? preparedReducerCreator
             : reducerCreator
         handler.handle(reducerDetails, reducerDefinition as any, contextMethods)

From a53a0c67159eb8ce576a9c4ba353d3e5f2128ac9 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sun, 18 Feb 2024 22:22:59 +0000
Subject: [PATCH 125/178] fix type imports

---
 errors.json                               |  3 ++-
 packages/toolkit/src/asyncThunkCreator.ts | 16 ++++++++--------
 2 files changed, 10 insertions(+), 9 deletions(-)

diff --git a/errors.json b/errors.json
index 284060ff88..b11c1b5461 100644
--- a/errors.json
+++ b/errors.json
@@ -42,5 +42,6 @@
   "40": "Please use reducer creators passed to callback. Each reducer definition must have a `_reducerDefinitionType` property indicating which handler to use.",
   "41": "Cannot use reserved creator type: ",
   "42": "Unsupported reducer type: ",
-  "43": "If provided, `asyncThunk` creator must be `asyncThunkCreator` from '@reduxjs/toolkit'"
+  "43": "If provided, `asyncThunk` creator must be `asyncThunkCreator` from '@reduxjs/toolkit'",
+  "44": "called \\`injectEndpoints\\` to override already-existing endpointName  without specifying \\`overrideExisting: true\\`"
 }
\ No newline at end of file
diff --git a/packages/toolkit/src/asyncThunkCreator.ts b/packages/toolkit/src/asyncThunkCreator.ts
index 4f596ac4b9..5255f247fa 100644
--- a/packages/toolkit/src/asyncThunkCreator.ts
+++ b/packages/toolkit/src/asyncThunkCreator.ts
@@ -1,3 +1,10 @@
+import type {
+  ReducerNamesOfType,
+  ReducerCreatorEntry,
+  ReducerCreator,
+  ReducerDefinition,
+  CreatorCaseReducers,
+} from '@reduxjs/toolkit'
 import type {
   AsyncThunk,
   AsyncThunkConfig,
@@ -7,13 +14,6 @@ import type {
 } from './createAsyncThunk'
 import { createAsyncThunk } from './createAsyncThunk'
 import type { CaseReducer } from './createReducer'
-import type {
-  CreatorCaseReducers,
-  ReducerCreator,
-  ReducerCreatorEntry,
-  ReducerDefinition,
-  ReducerNamesOfType,
-} from './createSlice'
 import { ReducerType } from './createSlice'
 import type { Id } from './tsHelpers'
 
@@ -21,7 +21,7 @@ declare module '@reduxjs/toolkit' {
   export interface SliceReducerCreators<
     State,
     CaseReducers extends CreatorCaseReducers<State>,
-    Name extends string = string,
+    Name extends string,
   > {
     [ReducerType.asyncThunk]: ReducerCreatorEntry<
       AsyncThunkCreator<State>,

From 09cc0c723ae88d5415b3d8941a56895c6c15ef03 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sun, 18 Feb 2024 22:31:47 +0000
Subject: [PATCH 126/178] split type test

---
 .../src/tests/asyncThunkCreator.test-d.ts     | 240 +++++++++++++++++
 .../toolkit/src/tests/createSlice.test-d.ts   | 252 ++----------------
 2 files changed, 265 insertions(+), 227 deletions(-)
 create mode 100644 packages/toolkit/src/tests/asyncThunkCreator.test-d.ts

diff --git a/packages/toolkit/src/tests/asyncThunkCreator.test-d.ts b/packages/toolkit/src/tests/asyncThunkCreator.test-d.ts
new file mode 100644
index 0000000000..d4cc476f28
--- /dev/null
+++ b/packages/toolkit/src/tests/asyncThunkCreator.test-d.ts
@@ -0,0 +1,240 @@
+import type {
+  ThunkDispatch,
+  SerializedError,
+  AsyncThunk,
+  CaseReducer,
+} from '@reduxjs/toolkit'
+import {
+  asyncThunkCreator,
+  buildCreateSlice,
+  isRejected,
+  configureStore,
+} from '@reduxjs/toolkit'
+
+describe('type tests', () => {
+  test('reducer callback', () => {
+    interface TestState {
+      foo: string
+    }
+
+    interface TestArg {
+      test: string
+    }
+
+    interface TestReturned {
+      payload: string
+    }
+
+    interface TestReject {
+      cause: string
+    }
+
+    const createAppSlice = buildCreateSlice({
+      creators: { asyncThunk: asyncThunkCreator },
+    })
+
+    const slice = createAppSlice({
+      name: 'test',
+      initialState: {} as TestState,
+      reducers: (create) => {
+        const preTypedAsyncThunk = create.asyncThunk.withTypes<{
+          rejectValue: TestReject
+        }>()
+
+        // @ts-expect-error
+        create.asyncThunk<any, any, { state: StoreState }>(() => {})
+
+        // @ts-expect-error
+        create.asyncThunk.withTypes<{
+          rejectValue: string
+          dispatch: StoreDispatch
+        }>()
+
+        return {
+          testInfer: create.asyncThunk(
+            function payloadCreator(arg: TestArg, api) {
+              return Promise.resolve<TestReturned>({ payload: 'foo' })
+            },
+            {
+              pending(state, action) {
+                expectTypeOf(state).toEqualTypeOf<TestState>()
+
+                expectTypeOf(action.meta.arg).toEqualTypeOf<TestArg>()
+              },
+              fulfilled(state, action) {
+                expectTypeOf(state).toEqualTypeOf<TestState>()
+
+                expectTypeOf(action.meta.arg).toEqualTypeOf<TestArg>()
+
+                expectTypeOf(action.payload).toEqualTypeOf<TestReturned>()
+              },
+              rejected(state, action) {
+                expectTypeOf(state).toEqualTypeOf<TestState>()
+
+                expectTypeOf(action.meta.arg).toEqualTypeOf<TestArg>()
+
+                expectTypeOf(action.error).toEqualTypeOf<SerializedError>()
+              },
+              settled(state, action) {
+                expectTypeOf(state).toEqualTypeOf<TestState>()
+
+                expectTypeOf(action.meta.arg).toEqualTypeOf<TestArg>()
+
+                if (isRejected(action)) {
+                  expectTypeOf(action.error).toEqualTypeOf<SerializedError>()
+                } else {
+                  expectTypeOf(action.payload).toEqualTypeOf<TestReturned>()
+                }
+              },
+            },
+          ),
+          testExplicitType: create.asyncThunk<
+            TestReturned,
+            TestArg,
+            {
+              rejectValue: TestReject
+            }
+          >(
+            function payloadCreator(arg, api) {
+              // here would be a circular reference
+              expectTypeOf(api.getState()).toBeUnknown()
+              // here would be a circular reference
+              expectTypeOf(api.dispatch).toMatchTypeOf<
+                ThunkDispatch<any, any, any>
+              >()
+
+              // so you need to cast inside instead
+              const getState = api.getState as () => StoreState
+              const dispatch = api.dispatch as StoreDispatch
+
+              expectTypeOf(arg).toEqualTypeOf<TestArg>()
+
+              expectTypeOf(api.rejectWithValue).toMatchTypeOf<
+                (value: TestReject) => any
+              >()
+
+              return Promise.resolve({ payload: 'foo' })
+            },
+            {
+              pending(state, action) {
+                expectTypeOf(state).toEqualTypeOf<TestState>()
+
+                expectTypeOf(action.meta.arg).toEqualTypeOf<TestArg>()
+              },
+              fulfilled(state, action) {
+                expectTypeOf(state).toEqualTypeOf<TestState>()
+
+                expectTypeOf(action.meta.arg).toEqualTypeOf<TestArg>()
+
+                expectTypeOf(action.payload).toEqualTypeOf<TestReturned>()
+              },
+              rejected(state, action) {
+                expectTypeOf(state).toEqualTypeOf<TestState>()
+
+                expectTypeOf(action.meta.arg).toEqualTypeOf<TestArg>()
+
+                expectTypeOf(action.error).toEqualTypeOf<SerializedError>()
+
+                expectTypeOf(action.payload).toEqualTypeOf<
+                  TestReject | undefined
+                >()
+              },
+              settled(state, action) {
+                expectTypeOf(state).toEqualTypeOf<TestState>()
+
+                expectTypeOf(action.meta.arg).toEqualTypeOf<TestArg>()
+
+                if (isRejected(action)) {
+                  expectTypeOf(action.error).toEqualTypeOf<SerializedError>()
+
+                  expectTypeOf(action.payload).toEqualTypeOf<
+                    TestReject | undefined
+                  >()
+                } else {
+                  expectTypeOf(action.payload).toEqualTypeOf<TestReturned>()
+                }
+              },
+            },
+          ),
+          testPreTyped: preTypedAsyncThunk(
+            function payloadCreator(arg: TestArg, api) {
+              expectTypeOf(api.rejectWithValue).toMatchTypeOf<
+                (value: TestReject) => any
+              >()
+
+              return Promise.resolve<TestReturned>({ payload: 'foo' })
+            },
+            {
+              pending(state, action) {
+                expectTypeOf(state).toEqualTypeOf<TestState>()
+
+                expectTypeOf(action.meta.arg).toEqualTypeOf<TestArg>()
+              },
+              fulfilled(state, action) {
+                expectTypeOf(state).toEqualTypeOf<TestState>()
+
+                expectTypeOf(action.meta.arg).toEqualTypeOf<TestArg>()
+
+                expectTypeOf(action.payload).toEqualTypeOf<TestReturned>()
+              },
+              rejected(state, action) {
+                expectTypeOf(state).toEqualTypeOf<TestState>()
+
+                expectTypeOf(action.meta.arg).toEqualTypeOf<TestArg>()
+
+                expectTypeOf(action.error).toEqualTypeOf<SerializedError>()
+
+                expectTypeOf(action.payload).toEqualTypeOf<
+                  TestReject | undefined
+                >()
+              },
+              settled(state, action) {
+                expectTypeOf(state).toEqualTypeOf<TestState>()
+
+                expectTypeOf(action.meta.arg).toEqualTypeOf<TestArg>()
+
+                if (isRejected(action)) {
+                  expectTypeOf(action.error).toEqualTypeOf<SerializedError>()
+
+                  expectTypeOf(action.payload).toEqualTypeOf<
+                    TestReject | undefined
+                  >()
+                } else {
+                  expectTypeOf(action.payload).toEqualTypeOf<TestReturned>()
+                }
+              },
+            },
+          ),
+        }
+      },
+    })
+
+    const store = configureStore({ reducer: { test: slice.reducer } })
+
+    type StoreState = ReturnType<typeof store.getState>
+
+    type StoreDispatch = typeof store.dispatch
+
+    expectTypeOf(slice.actions.testInfer).toEqualTypeOf<
+      AsyncThunk<TestReturned, TestArg, {}>
+    >()
+
+    expectTypeOf(slice.actions.testExplicitType).toEqualTypeOf<
+      AsyncThunk<TestReturned, TestArg, { rejectValue: TestReject }>
+    >()
+
+    type TestInferThunk = AsyncThunk<TestReturned, TestArg, {}>
+
+    expectTypeOf(slice.caseReducers.testInfer.pending).toEqualTypeOf<
+      CaseReducer<TestState, ReturnType<TestInferThunk['pending']>>
+    >()
+
+    expectTypeOf(slice.caseReducers.testInfer.fulfilled).toEqualTypeOf<
+      CaseReducer<TestState, ReturnType<TestInferThunk['fulfilled']>>
+    >()
+
+    expectTypeOf(slice.caseReducers.testInfer.rejected).toEqualTypeOf<
+      CaseReducer<TestState, ReturnType<TestInferThunk['rejected']>>
+    >()
+  })
+})
diff --git a/packages/toolkit/src/tests/createSlice.test-d.ts b/packages/toolkit/src/tests/createSlice.test-d.ts
index 8519ef0a45..d8b52dbf11 100644
--- a/packages/toolkit/src/tests/createSlice.test-d.ts
+++ b/packages/toolkit/src/tests/createSlice.test-d.ts
@@ -6,8 +6,6 @@ import type {
   ActionCreatorWithPreparedPayload,
   ActionCreatorWithoutPayload,
   ActionReducerMapBuilder,
-  AsyncThunk,
-  CaseReducer,
   CreatorCaseReducers,
   PayloadAction,
   PayloadActionCreator,
@@ -18,11 +16,9 @@ import type {
   ReducerDefinition,
   ReducerHandlingContextMethods,
   ReducerNamesOfType,
-  SerializedError,
   SliceActionType,
   SliceCaseReducers,
   ThunkAction,
-  ThunkDispatch,
   UnknownAction,
   ValidateSliceCaseReducers,
 } from '@reduxjs/toolkit'
@@ -33,7 +29,6 @@ import {
   createAction,
   createAsyncThunk,
   createSlice,
-  isRejected,
   nanoid,
 } from '@reduxjs/toolkit'
 import { castDraft } from 'immer'
@@ -614,215 +609,40 @@ describe('type tests', () => {
       cause: string
     }
 
-    const createAppSlice = buildCreateSlice({
-      creators: { asyncThunk: asyncThunkCreator },
-    })
-
-    const slice = createAppSlice({
+    const slice = createSlice({
       name: 'test',
       initialState: {} as TestState,
-      reducers: (create) => {
-        const preTypedAsyncThunk = create.asyncThunk.withTypes<{
-          rejectValue: TestReject
-        }>()
-
-        // @ts-expect-error
-        create.asyncThunk<any, any, { state: StoreState }>(() => {})
-
-        // @ts-expect-error
-        create.asyncThunk.withTypes<{
-          rejectValue: string
-          dispatch: StoreDispatch
-        }>()
+      reducers: (create) => ({
+        normalReducer: create.reducer<string>((state, action) => {
+          expectTypeOf(state).toEqualTypeOf<TestState>()
 
-        return {
-          normalReducer: create.reducer<string>((state, action) => {
-            expectTypeOf(state).toEqualTypeOf<TestState>()
+          expectTypeOf(action.payload).toBeString()
+        }),
+        optionalReducer: create.reducer<string | undefined>((state, action) => {
+          expectTypeOf(state).toEqualTypeOf<TestState>()
 
-            expectTypeOf(action.payload).toBeString()
+          expectTypeOf(action.payload).toEqualTypeOf<string | undefined>()
+        }),
+        noActionReducer: create.reducer((state) => {
+          expectTypeOf(state).toEqualTypeOf<TestState>()
+        }),
+        preparedReducer: create.preparedReducer(
+          (payload: string) => ({
+            payload,
+            meta: 'meta' as const,
+            error: 'error' as const,
           }),
-          optionalReducer: create.reducer<string | undefined>(
-            (state, action) => {
-              expectTypeOf(state).toEqualTypeOf<TestState>()
-
-              expectTypeOf(action.payload).toEqualTypeOf<string | undefined>()
-            },
-          ),
-          noActionReducer: create.reducer((state) => {
+          (state, action) => {
             expectTypeOf(state).toEqualTypeOf<TestState>()
-          }),
-          preparedReducer: create.preparedReducer(
-            (payload: string) => ({
-              payload,
-              meta: 'meta' as const,
-              error: 'error' as const,
-            }),
-            (state, action) => {
-              expectTypeOf(state).toEqualTypeOf<TestState>()
-
-              expectTypeOf(action.payload).toBeString()
-
-              expectTypeOf(action.meta).toEqualTypeOf<'meta'>()
-
-              expectTypeOf(action.error).toEqualTypeOf<'error'>()
-            },
-          ),
-          testInfer: create.asyncThunk(
-            function payloadCreator(arg: TestArg, api) {
-              return Promise.resolve<TestReturned>({ payload: 'foo' })
-            },
-            {
-              pending(state, action) {
-                expectTypeOf(state).toEqualTypeOf<TestState>()
-
-                expectTypeOf(action.meta.arg).toEqualTypeOf<TestArg>()
-              },
-              fulfilled(state, action) {
-                expectTypeOf(state).toEqualTypeOf<TestState>()
-
-                expectTypeOf(action.meta.arg).toEqualTypeOf<TestArg>()
-
-                expectTypeOf(action.payload).toEqualTypeOf<TestReturned>()
-              },
-              rejected(state, action) {
-                expectTypeOf(state).toEqualTypeOf<TestState>()
-
-                expectTypeOf(action.meta.arg).toEqualTypeOf<TestArg>()
-
-                expectTypeOf(action.error).toEqualTypeOf<SerializedError>()
-              },
-              settled(state, action) {
-                expectTypeOf(state).toEqualTypeOf<TestState>()
-
-                expectTypeOf(action.meta.arg).toEqualTypeOf<TestArg>()
-
-                if (isRejected(action)) {
-                  expectTypeOf(action.error).toEqualTypeOf<SerializedError>()
-                } else {
-                  expectTypeOf(action.payload).toEqualTypeOf<TestReturned>()
-                }
-              },
-            },
-          ),
-          testExplicitType: create.asyncThunk<
-            TestReturned,
-            TestArg,
-            {
-              rejectValue: TestReject
-            }
-          >(
-            function payloadCreator(arg, api) {
-              // here would be a circular reference
-              expectTypeOf(api.getState()).toBeUnknown()
-              // here would be a circular reference
-              expectTypeOf(api.dispatch).toMatchTypeOf<
-                ThunkDispatch<any, any, any>
-              >()
-
-              // so you need to cast inside instead
-              const getState = api.getState as () => StoreState
-              const dispatch = api.dispatch as StoreDispatch
-
-              expectTypeOf(arg).toEqualTypeOf<TestArg>()
-
-              expectTypeOf(api.rejectWithValue).toMatchTypeOf<
-                (value: TestReject) => any
-              >()
-
-              return Promise.resolve({ payload: 'foo' })
-            },
-            {
-              pending(state, action) {
-                expectTypeOf(state).toEqualTypeOf<TestState>()
-
-                expectTypeOf(action.meta.arg).toEqualTypeOf<TestArg>()
-              },
-              fulfilled(state, action) {
-                expectTypeOf(state).toEqualTypeOf<TestState>()
 
-                expectTypeOf(action.meta.arg).toEqualTypeOf<TestArg>()
-
-                expectTypeOf(action.payload).toEqualTypeOf<TestReturned>()
-              },
-              rejected(state, action) {
-                expectTypeOf(state).toEqualTypeOf<TestState>()
-
-                expectTypeOf(action.meta.arg).toEqualTypeOf<TestArg>()
-
-                expectTypeOf(action.error).toEqualTypeOf<SerializedError>()
-
-                expectTypeOf(action.payload).toEqualTypeOf<
-                  TestReject | undefined
-                >()
-              },
-              settled(state, action) {
-                expectTypeOf(state).toEqualTypeOf<TestState>()
-
-                expectTypeOf(action.meta.arg).toEqualTypeOf<TestArg>()
-
-                if (isRejected(action)) {
-                  expectTypeOf(action.error).toEqualTypeOf<SerializedError>()
-
-                  expectTypeOf(action.payload).toEqualTypeOf<
-                    TestReject | undefined
-                  >()
-                } else {
-                  expectTypeOf(action.payload).toEqualTypeOf<TestReturned>()
-                }
-              },
-            },
-          ),
-          testPreTyped: preTypedAsyncThunk(
-            function payloadCreator(arg: TestArg, api) {
-              expectTypeOf(api.rejectWithValue).toMatchTypeOf<
-                (value: TestReject) => any
-              >()
-
-              return Promise.resolve<TestReturned>({ payload: 'foo' })
-            },
-            {
-              pending(state, action) {
-                expectTypeOf(state).toEqualTypeOf<TestState>()
-
-                expectTypeOf(action.meta.arg).toEqualTypeOf<TestArg>()
-              },
-              fulfilled(state, action) {
-                expectTypeOf(state).toEqualTypeOf<TestState>()
-
-                expectTypeOf(action.meta.arg).toEqualTypeOf<TestArg>()
-
-                expectTypeOf(action.payload).toEqualTypeOf<TestReturned>()
-              },
-              rejected(state, action) {
-                expectTypeOf(state).toEqualTypeOf<TestState>()
-
-                expectTypeOf(action.meta.arg).toEqualTypeOf<TestArg>()
-
-                expectTypeOf(action.error).toEqualTypeOf<SerializedError>()
-
-                expectTypeOf(action.payload).toEqualTypeOf<
-                  TestReject | undefined
-                >()
-              },
-              settled(state, action) {
-                expectTypeOf(state).toEqualTypeOf<TestState>()
-
-                expectTypeOf(action.meta.arg).toEqualTypeOf<TestArg>()
+            expectTypeOf(action.payload).toBeString()
 
-                if (isRejected(action)) {
-                  expectTypeOf(action.error).toEqualTypeOf<SerializedError>()
+            expectTypeOf(action.meta).toEqualTypeOf<'meta'>()
 
-                  expectTypeOf(action.payload).toEqualTypeOf<
-                    TestReject | undefined
-                  >()
-                } else {
-                  expectTypeOf(action.payload).toEqualTypeOf<TestReturned>()
-                }
-              },
-            },
-          ),
-        }
-      },
+            expectTypeOf(action.error).toEqualTypeOf<'error'>()
+          },
+        ),
+      }),
     })
 
     const store = configureStore({ reducer: { test: slice.reducer } })
@@ -870,28 +690,6 @@ describe('type tests', () => {
         'meta'
       >
     >()
-
-    expectTypeOf(slice.actions.testInfer).toEqualTypeOf<
-      AsyncThunk<TestReturned, TestArg, {}>
-    >()
-
-    expectTypeOf(slice.actions.testExplicitType).toEqualTypeOf<
-      AsyncThunk<TestReturned, TestArg, { rejectValue: TestReject }>
-    >()
-
-    type TestInferThunk = AsyncThunk<TestReturned, TestArg, {}>
-
-    expectTypeOf(slice.caseReducers.testInfer.pending).toEqualTypeOf<
-      CaseReducer<TestState, ReturnType<TestInferThunk['pending']>>
-    >()
-
-    expectTypeOf(slice.caseReducers.testInfer.fulfilled).toEqualTypeOf<
-      CaseReducer<TestState, ReturnType<TestInferThunk['fulfilled']>>
-    >()
-
-    expectTypeOf(slice.caseReducers.testInfer.rejected).toEqualTypeOf<
-      CaseReducer<TestState, ReturnType<TestInferThunk['rejected']>>
-    >()
   })
 
   test('wrapping createSlice should be possible, with callback', () => {

From 7252c16413d57a1d96d943b4b9dc27e87a8d58a5 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sun, 18 Feb 2024 22:57:18 +0000
Subject: [PATCH 127/178] move module augmentation back to createSlice

---
 packages/toolkit/src/asyncThunkCreator.ts | 56 +----------------------
 packages/toolkit/src/createSlice.ts       | 41 +++++++++++++++++
 2 files changed, 43 insertions(+), 54 deletions(-)

diff --git a/packages/toolkit/src/asyncThunkCreator.ts b/packages/toolkit/src/asyncThunkCreator.ts
index 5255f247fa..dfefb82719 100644
--- a/packages/toolkit/src/asyncThunkCreator.ts
+++ b/packages/toolkit/src/asyncThunkCreator.ts
@@ -1,10 +1,3 @@
-import type {
-  ReducerNamesOfType,
-  ReducerCreatorEntry,
-  ReducerCreator,
-  ReducerDefinition,
-  CreatorCaseReducers,
-} from '@reduxjs/toolkit'
 import type {
   AsyncThunk,
   AsyncThunkConfig,
@@ -14,53 +7,8 @@ import type {
 } from './createAsyncThunk'
 import { createAsyncThunk } from './createAsyncThunk'
 import type { CaseReducer } from './createReducer'
+import type { ReducerCreator, ReducerDefinition } from './createSlice'
 import { ReducerType } from './createSlice'
-import type { Id } from './tsHelpers'
-
-declare module '@reduxjs/toolkit' {
-  export interface SliceReducerCreators<
-    State,
-    CaseReducers extends CreatorCaseReducers<State>,
-    Name extends string,
-  > {
-    [ReducerType.asyncThunk]: ReducerCreatorEntry<
-      AsyncThunkCreator<State>,
-      {
-        actions: {
-          [ReducerName in ReducerNamesOfType<
-            CaseReducers,
-            ReducerType.asyncThunk
-          >]: CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition<
-            State,
-            infer ThunkArg,
-            infer Returned,
-            infer ThunkApiConfig
-          >
-            ? AsyncThunk<Returned, ThunkArg, ThunkApiConfig>
-            : never
-        }
-        caseReducers: {
-          [ReducerName in ReducerNamesOfType<
-            CaseReducers,
-            ReducerType.asyncThunk
-          >]: CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition<
-            State,
-            any,
-            any,
-            any
-          >
-            ? Id<
-                Pick<
-                  Required<CaseReducers[ReducerName]>,
-                  'fulfilled' | 'rejected' | 'pending' | 'settled'
-                >
-              >
-            : never
-        }
-      }
-    >
-  }
-}
 
 export interface AsyncThunkSliceReducerConfig<
   State,
@@ -113,7 +61,7 @@ type PreventCircular<ThunkApiConfig> = {
     : ThunkApiConfig[K]
 }
 
-interface AsyncThunkCreator<
+export interface AsyncThunkCreator<
   State,
   CurriedThunkApiConfig extends
     PreventCircular<AsyncThunkConfig> = PreventCircular<AsyncThunkConfig>,
diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 524dd8be31..de35a831ed 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -1,5 +1,9 @@
 import type { Action, UnknownAction, Reducer } from 'redux'
 import type { Selector } from 'reselect'
+import type {
+  AsyncThunkCreator,
+  AsyncThunkSliceReducerDefinition,
+} from './asyncThunkCreator'
 import type {
   ActionCreatorWithoutPayload,
   PayloadAction,
@@ -8,6 +12,7 @@ import type {
   _ActionCreatorWithPreparedPayload,
 } from './createAction'
 import { createAction } from './createAction'
+import type { AsyncThunk } from './createAsyncThunk'
 import type {
   ActionMatcherDescriptionCollection,
   CaseReducer,
@@ -124,6 +129,42 @@ export interface SliceReducerCreators<
       }
     }
   >
+  [ReducerType.asyncThunk]: ReducerCreatorEntry<
+    AsyncThunkCreator<State>,
+    {
+      actions: {
+        [ReducerName in ReducerNamesOfType<
+          CaseReducers,
+          ReducerType.asyncThunk
+        >]: CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition<
+          State,
+          infer ThunkArg,
+          infer Returned,
+          infer ThunkApiConfig
+        >
+          ? AsyncThunk<Returned, ThunkArg, ThunkApiConfig>
+          : never
+      }
+      caseReducers: {
+        [ReducerName in ReducerNamesOfType<
+          CaseReducers,
+          ReducerType.asyncThunk
+        >]: CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition<
+          State,
+          any,
+          any,
+          any
+        >
+          ? Id<
+              Pick<
+                Required<CaseReducers[ReducerName]>,
+                'fulfilled' | 'rejected' | 'pending' | 'settled'
+              >
+            >
+          : never
+      }
+    }
+  >
 }
 
 export type ReducerCreators<

From 5f5fe7952630acc7b28de4d0e371522a23922cba Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sun, 18 Feb 2024 23:27:47 +0000
Subject: [PATCH 128/178] Rework mapped type to keep homogenous mapping

---
 packages/toolkit/src/createSlice.ts           | 79 ++++++++++---------
 .../toolkit/src/tests/createSlice.test-d.ts   |  8 +-
 .../toolkit/src/tests/createSlice.test.ts     | 28 +++----
 3 files changed, 58 insertions(+), 57 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index de35a831ed..d133262c01 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -24,6 +24,7 @@ import { executeReducerBuilderCallback } from './mapBuilders'
 import type { Id, TypeGuard, UnionToIntersection } from './tsHelpers'
 import type { InjectConfig } from './combineSlices'
 import { emplace } from './utils'
+import { DistributiveOmit } from 'react-redux'
 
 export enum ReducerType {
   reducer = 'reducer',
@@ -75,23 +76,23 @@ export interface SliceReducerCreators<
     },
     {
       actions: {
-        [ReducerName in keyof CaseReducers as CaseReducers[ReducerName] extends CaseReducer<
+        [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends CaseReducer<
           State,
           any
         >
-          ? ReducerName
-          : never]: ActionCreatorForCaseReducer<
-          CaseReducers[ReducerName],
-          SliceActionType<Name, ReducerName>
-        >
+          ? ActionCreatorForCaseReducer<
+              CaseReducers[ReducerName],
+              SliceActionType<Name, ReducerName>
+            >
+          : never
       }
       caseReducers: {
-        [ReducerName in keyof CaseReducers as CaseReducers[ReducerName] extends CaseReducer<
+        [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends CaseReducer<
           State,
           any
         >
-          ? ReducerName
-          : never]: CaseReducers[ReducerName]
+          ? CaseReducers[ReducerName]
+          : never
       }
     }
   >
@@ -105,26 +106,26 @@ export interface SliceReducerCreators<
     ) => PreparedCaseReducerDefinition<State, Prepare>,
     {
       actions: {
-        [ReducerName in keyof CaseReducers as CaseReducers[ReducerName] extends CaseReducerWithPrepare<
+        [ReducerName in keyof CaseReducers as ReducerName]: CaseReducers[ReducerName] extends CaseReducerWithPrepare<
           State,
           any
         >
-          ? ReducerName
-          : never]: CaseReducers[ReducerName] extends { prepare: any }
-          ? ActionCreatorForCaseReducerWithPrepare<
-              CaseReducers[ReducerName],
-              SliceActionType<Name, ReducerName>
-            >
+          ? CaseReducers[ReducerName] extends { prepare: any }
+            ? ActionCreatorForCaseReducerWithPrepare<
+                CaseReducers[ReducerName],
+                SliceActionType<Name, ReducerName>
+              >
+            : never
           : never
       }
       caseReducers: {
-        [ReducerName in keyof CaseReducers as CaseReducers[ReducerName] extends CaseReducerWithPrepare<
+        [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends CaseReducerWithPrepare<
           State,
           any
         >
-          ? ReducerName
-          : never]: CaseReducers[ReducerName] extends { reducer: infer Reducer }
-          ? Reducer
+          ? CaseReducers[ReducerName] extends { reducer: infer Reducer }
+            ? Reducer
+            : never
           : never
       }
     }
@@ -133,10 +134,7 @@ export interface SliceReducerCreators<
     AsyncThunkCreator<State>,
     {
       actions: {
-        [ReducerName in ReducerNamesOfType<
-          CaseReducers,
-          ReducerType.asyncThunk
-        >]: CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition<
+        [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition<
           State,
           infer ThunkArg,
           infer Returned,
@@ -146,10 +144,7 @@ export interface SliceReducerCreators<
           : never
       }
       caseReducers: {
-        [ReducerName in ReducerNamesOfType<
-          CaseReducers,
-          ReducerType.asyncThunk
-        >]: CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition<
+        [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition<
           State,
           any,
           any,
@@ -631,6 +626,10 @@ export type SliceActionType<
   ActionName extends keyof any,
 > = ActionName extends string | number ? `${SliceName}/${ActionName}` : string
 
+type ConvertNeverKeysToUnknown<T> = T extends any
+  ? { [K in keyof T]: T[K] extends never ? unknown : T[K] }
+  : never
+
 /**
  * Derives the slice's `actions` property from the `reducers` options
  *
@@ -642,11 +641,13 @@ export type CaseReducerActions<
   State = any,
 > = Id<
   UnionToIntersection<
-    SliceReducerCreators<
-      State,
-      CaseReducers,
-      SliceName
-    >[RegisteredReducerType]['actions']
+    ConvertNeverKeysToUnknown<
+      SliceReducerCreators<
+        State,
+        CaseReducers,
+        SliceName
+      >[RegisteredReducerType]['actions']
+    >
   >
 >
 
@@ -686,11 +687,13 @@ type SliceDefinedCaseReducers<
   State = any,
 > = Id<
   UnionToIntersection<
-    SliceReducerCreators<
-      State,
-      CaseReducers,
-      SliceName
-    >[RegisteredReducerType]['caseReducers']
+    ConvertNeverKeysToUnknown<
+      SliceReducerCreators<
+        State,
+        CaseReducers,
+        SliceName
+      >[RegisteredReducerType]['caseReducers']
+    >
   >
 >
 
diff --git a/packages/toolkit/src/tests/createSlice.test-d.ts b/packages/toolkit/src/tests/createSlice.test-d.ts
index d8b52dbf11..a586f875d8 100644
--- a/packages/toolkit/src/tests/createSlice.test-d.ts
+++ b/packages/toolkit/src/tests/createSlice.test-d.ts
@@ -15,7 +15,6 @@ import type {
   ReducerCreators,
   ReducerDefinition,
   ReducerHandlingContextMethods,
-  ReducerNamesOfType,
   SliceActionType,
   SliceCaseReducers,
   ThunkAction,
@@ -894,10 +893,11 @@ declare module '@reduxjs/toolkit' {
         : never,
       {
         actions: {
-          [ReducerName in ReducerNamesOfType<
-            CaseReducers,
+          [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends ReducerDefinition<
             typeof toasterCreatorType
-          >]: AddToastThunk<Name, ReducerName>
+          >
+            ? AddToastThunk<Name, ReducerName>
+            : never
         }
       }
     >
diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index e2a20e8d3c..f90b092056 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -12,13 +12,11 @@ import type {
   ReducerCreatorEntry,
   ReducerCreators,
   ReducerDefinition,
-  ReducerNamesOfType,
   SliceActionType,
   ThunkAction,
   WithSlice,
 } from '@reduxjs/toolkit'
 import {
-  asyncThunkCreator,
   buildCreateSlice,
   combineSlices,
   configureStore,
@@ -998,18 +996,20 @@ declare module '@reduxjs/toolkit' {
       ) => LoaderReducerDefinition<State>,
       {
         actions: {
-          [ReducerName in ReducerNamesOfType<
-            CaseReducers,
+          [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends ReducerDefinition<
             typeof loaderCreatorType
-          >]: LoaderThunk<Name, ReducerName>
+          >
+            ? LoaderThunk<Name, ReducerName>
+            : never
         }
         caseReducers: {
-          [ReducerName in ReducerNamesOfType<
-            CaseReducers,
+          [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends ReducerDefinition<
             typeof loaderCreatorType
-          >]: Required<
-            Pick<LoaderReducerDefinition<State>, 'ended' | 'started'>
           >
+            ? Required<
+                Pick<LoaderReducerDefinition<State>, 'ended' | 'started'>
+              >
+            : never
         }
       }
     >
@@ -1025,18 +1025,16 @@ declare module '@reduxjs/toolkit' {
         : never,
       {
         actions: {
-          [ReducerName in ReducerNamesOfType<
-            CaseReducers,
+          [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends ReducerDefinition<
             typeof historyMethodsCreatorType
-          >]: CaseReducers[ReducerName] extends { type: 'reset' }
+          > & { type: 'reset' }
             ? PayloadActionCreator<void, SliceActionType<Name, ReducerName>>
             : never
         }
         caseReducers: {
-          [ReducerName in ReducerNamesOfType<
-            CaseReducers,
+          [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends ReducerDefinition<
             typeof historyMethodsCreatorType
-          >]: CaseReducers[ReducerName] extends { type: 'reset' }
+          > & { type: 'reset' }
             ? CaseReducer<State, PayloadAction>
             : never
         }

From 31c4794a6137dbd42fa2a94012c16aab62e5001a Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sun, 18 Feb 2024 23:47:10 +0000
Subject: [PATCH 129/178] there are no entity methods creators in ba sing se

---
 docs/api/createEntityAdapter.mdx              |   2 +-
 docs/api/createSlice.mdx                      |  99 -----------
 packages/toolkit/src/entities/models.ts       |  72 ++------
 .../toolkit/src/entities/slice_creator.ts     | 161 ------------------
 .../toolkit/src/entities/state_selectors.ts   |  92 +++-------
 .../tests/entity_slice_enhancer.test-d.ts     |  74 --------
 .../tests/entity_slice_enhancer.test.ts       |  98 +----------
 .../entities/tests/state_selectors.test.ts    |  55 ------
 packages/toolkit/src/index.ts                 |   5 -
 9 files changed, 37 insertions(+), 621 deletions(-)
 delete mode 100644 packages/toolkit/src/entities/slice_creator.ts
 delete mode 100644 packages/toolkit/src/entities/tests/entity_slice_enhancer.test-d.ts

diff --git a/docs/api/createEntityAdapter.mdx b/docs/api/createEntityAdapter.mdx
index e74a7349ab..d08d145bc9 100644
--- a/docs/api/createEntityAdapter.mdx
+++ b/docs/api/createEntityAdapter.mdx
@@ -239,7 +239,7 @@ In other words, they accept a state that looks like `{ids: [], entities: {}}`, a
 
 These CRUD methods may be used in multiple ways:
 
-- They may be passed as case reducers directly to `createReducer` and `createSlice`. (also see the [`create.entityMethods`](./createSlice#createentitymethods-entitymethodscreator) slice creator which can assist with this)
+- They may be passed as case reducers directly to `createReducer` and `createSlice`.
 - They may be used as "mutating" helper methods when called manually, such as a separate hand-written call to `addOne()` inside of an existing case reducer, if the `state` argument is actually an Immer `Draft` value.
 - They may be used as immutable update methods when called manually, if the `state` argument is actually a plain JS object or array.
 
diff --git a/docs/api/createSlice.mdx b/docs/api/createSlice.mdx
index ef5e5a925d..64326aa2e1 100644
--- a/docs/api/createSlice.mdx
+++ b/docs/api/createSlice.mdx
@@ -845,105 +845,6 @@ reducers: (create) => {
 
 :::
 
-##### `create.entityMethods` (`entityMethodsCreator`)
-
-Creates a set of reducers for managing a normalized entity state, based on a provided [adapter](./createEntityAdapter).
-
-```ts
-import {
-  createEntityAdapter,
-  buildCreateSlice,
-  entityMethodsCreator,
-} from '@reduxjs/toolkit'
-
-const createAppSlice = buildCreateSlice({
-  creators: { entityMethods: entityMethodsCreator },
-})
-
-interface Post {
-  id: string
-  text: string
-}
-
-const postsAdapter = createEntityAdapter<Post>()
-
-const postsSlice = createAppSlice({
-  name: 'posts',
-  initialState: postsAdapter.getInitialState(),
-  reducers: (create) => ({
-    ...create.entityMethods(postsAdapter),
-  }),
-})
-
-export const { setOne, upsertMany, removeAll, ...etc } = postsSlice.actions
-```
-
-:::caution
-
-Because this creator returns an object of multiple reducer definitions, it should be spread into the final object returned by the `reducers` callback.
-
-:::
-
-**Parameters**
-
-- **adapter** The [adapter](./createEntityAdapter) to use.
-- **config** The configuration object. (optional)
-
-The configuration object can contain some of the following options:
-
-**`selectEntityState`**
-
-A selector to retrieve the entity state from the slice state. Defaults to `state => state`, but should be provided if the entity state is nested.
-
-```ts no-transpile
-const postsSlice = createAppSlice({
-  name: 'posts',
-  initialState: { posts: postsAdapter.getInitialState() },
-  reducers: (create) => ({
-    ...create.entityMethods(postsAdapter, {
-      selectEntityState: (state) => state.posts,
-    }),
-  }),
-})
-```
-
-**`name`, `pluralName`**
-
-It's often desirable to modify the reducer names to be specific to the data type being used. These options allow you to do that.
-
-```ts no-transpile
-const postsSlice = createAppSlice({
-  name: 'posts',
-  initialState: postsAdapter.getInitialState(),
-  reducers: (create) => ({
-    ...create.entityMethods(postsAdapter, {
-      name: 'post',
-    }),
-  }),
-})
-
-const { addOnePost, upsertManyPosts, removeAllPosts, ...etc } =
-  postsSlice.actions
-```
-
-`pluralName` defaults to `name + 's'`, but can be provided if this isn't desired.
-
-```ts no-transpile
-const gooseSlice = createAppSlice({
-  name: 'geese',
-  initialState: gooseAdapter.getInitialState(),
-  reducers: (create) => ({
-    ...create.entityMethods(gooseAdapter, {
-      name: 'goose',
-      pluralName: 'geese',
-    }),
-  }),
-})
-
-const { addOneGoose, upsertManyGeese, removeAllGeese, ...etc } =
-  gooseSlice.actions
-```
-
 ### Writing your own creators
 
 In version v2.2.0 (TODO), we introduced a system for including your own creators.
diff --git a/packages/toolkit/src/entities/models.ts b/packages/toolkit/src/entities/models.ts
index 8706a143ed..76de9872fd 100644
--- a/packages/toolkit/src/entities/models.ts
+++ b/packages/toolkit/src/entities/models.ts
@@ -3,8 +3,6 @@ import type { Draft } from 'immer'
 import type { PayloadAction } from '../createAction'
 import type { GetSelectorsOptions } from './state_selectors'
 import type { CastAny, Id as Compute } from '../tsHelpers'
-import type { CaseReducerDefinition } from '../createSlice'
-import type { CaseReducer } from '../createReducer'
 
 /**
  * @public
@@ -160,51 +158,12 @@ export interface EntityStateAdapter<T, Id extends EntityId> {
 /**
  * @public
  */
-export type EntitySelectors<
-  T,
-  V,
-  Id extends EntityId,
-  Single extends string = '',
-  Plural extends string = DefaultPlural<Single>,
-> = Compute<
-  {
-    [K in `select${Capitalize<Single>}Ids`]: (state: V) => Id[]
-  } & {
-    [K in `select${Capitalize<Single>}Entities`]: (state: V) => Record<Id, T>
-  } & {
-    [K in `selectAll${Capitalize<Plural>}`]: (state: V) => T[]
-  } & {
-    [K in `selectTotal${Capitalize<Plural>}`]: (state: V) => number
-  } & {
-    [K in `select${Capitalize<Single>}ById`]: (
-      state: V,
-      id: Id,
-    ) => Compute<UncheckedIndexedAccess<T>>
-  }
->
-
-export type DefaultPlural<Single extends string> = Single extends ''
-  ? ''
-  : `${Single}s`
-
-export type EntityReducers<
-  T,
-  Id extends EntityId,
-  State = EntityState<T, Id>,
-  Single extends string = '',
-  Plural extends string = DefaultPlural<Single>,
-> = {
-  [K in keyof EntityStateAdapter<
-    T,
-    Id
-  > as `${K}${Capitalize<K extends `${string}One` ? Single : Plural>}`]: EntityStateAdapter<
-    T,
-    Id
-  >[K] extends (state: any) => any
-    ? CaseReducerDefinition<State, PayloadAction>
-    : EntityStateAdapter<T, Id>[K] extends CaseReducer<any, infer A>
-      ? CaseReducerDefinition<State, A>
-      : never
+export interface EntitySelectors<T, V, Id extends EntityId> {
+  selectIds: (state: V) => Id[]
+  selectEntities: (state: V) => Record<Id, T>
+  selectAll: (state: V) => T[]
+  selectTotal: (state: V) => number
+  selectById: (state: V, id: Id) => Compute<UncheckedIndexedAccess<T>>
 }
 
 /**
@@ -228,19 +187,12 @@ export interface EntityAdapter<T, Id extends EntityId>
   extends EntityStateAdapter<T, Id>,
     EntityStateFactory<T, Id>,
     Required<EntityAdapterOptions<T, Id>> {
-  getSelectors<
-    Single extends string = '',
-    Plural extends string = DefaultPlural<Single>,
-  >(
+  getSelectors(
     selectState?: undefined,
-    options?: GetSelectorsOptions<Single, Plural>,
-  ): EntitySelectors<T, EntityState<T, Id>, Id, Single, Plural>
-  getSelectors<
-    V,
-    Single extends string = '',
-    Plural extends string = DefaultPlural<Single>,
-  >(
+    options?: GetSelectorsOptions,
+  ): EntitySelectors<T, EntityState<T, Id>, Id>
+  getSelectors<V>(
     selectState: (state: V) => EntityState<T, Id>,
-    options?: GetSelectorsOptions<Single, Plural>,
-  ): EntitySelectors<T, V, Id, Single, Plural>
+    options?: GetSelectorsOptions,
+  ): EntitySelectors<T, V, Id>
 }
diff --git a/packages/toolkit/src/entities/slice_creator.ts b/packages/toolkit/src/entities/slice_creator.ts
deleted file mode 100644
index b50ee26d10..0000000000
--- a/packages/toolkit/src/entities/slice_creator.ts
+++ /dev/null
@@ -1,161 +0,0 @@
-import type {
-  CreatorCaseReducers,
-  ReducerCreator,
-  ReducerCreatorEntry,
-} from '@reduxjs/toolkit'
-import { reducerCreator } from '../createSlice'
-import type { WithRequiredProp } from '../tsHelpers'
-import type {
-  Update,
-  EntityAdapter,
-  EntityId,
-  EntityState,
-  DefaultPlural,
-  EntityReducers,
-} from './models'
-import { capitalize } from './utils'
-
-export const entityMethodsCreatorType = /*@__PURE__*/ Symbol()
-
-export interface EntityMethodsCreatorConfig<
-  T,
-  Id extends EntityId,
-  State,
-  Single extends string,
-  Plural extends string,
-> {
-  selectEntityState?: (state: State) => EntityState<T, Id>
-  name?: Single
-  pluralName?: Plural
-}
-
-type EntityMethodsCreator<State> =
-  State extends EntityState<infer T, infer Id>
-    ? {
-        <
-          T,
-          Id extends EntityId,
-          Single extends string = '',
-          Plural extends string = DefaultPlural<Single>,
-        >(
-          adapter: EntityAdapter<T, Id>,
-          config: WithRequiredProp<
-            EntityMethodsCreatorConfig<T, Id, State, Single, Plural>,
-            'selectEntityState'
-          >,
-        ): EntityReducers<T, Id, State, Single, Plural>
-        <
-          Single extends string = '',
-          Plural extends string = DefaultPlural<Single>,
-        >(
-          adapter: EntityAdapter<T, Id>,
-          config?: Omit<
-            EntityMethodsCreatorConfig<T, Id, State, Single, Plural>,
-            'selectEntityState'
-          >,
-        ): EntityReducers<T, Id, State, Single, Plural>
-      }
-    : <
-        T,
-        Id extends EntityId,
-        Single extends string = '',
-        Plural extends string = DefaultPlural<Single>,
-      >(
-        adapter: EntityAdapter<T, Id>,
-        config: WithRequiredProp<
-          EntityMethodsCreatorConfig<T, Id, State, Single, Plural>,
-          'selectEntityState'
-        >,
-      ) => EntityReducers<T, Id, State, Single, Plural>
-
-declare module '@reduxjs/toolkit' {
-  export interface SliceReducerCreators<
-    State,
-    CaseReducers extends CreatorCaseReducers<State>,
-    Name extends string,
-  > {
-    [entityMethodsCreatorType]: ReducerCreatorEntry<EntityMethodsCreator<State>>
-  }
-}
-
-export function createEntityMethods<
-  T,
-  Id extends EntityId,
-  State = EntityState<T, Id>,
-  Single extends string = '',
-  Plural extends string = DefaultPlural<Single>,
->(
-  adapter: EntityAdapter<T, Id>,
-  {
-    selectEntityState = (state) => state as unknown as EntityState<T, Id>,
-    name: nameParam = '' as Single,
-    pluralName: pluralParam = (nameParam && `${nameParam}s`) as Plural,
-  }: EntityMethodsCreatorConfig<T, Id, State, Single, Plural> = {},
-): EntityReducers<T, Id, State, Single, Plural> {
-  // template literal computed keys don't keep their type if there's an unresolved generic
-  // so we cast to some intermediate type to at least check we're using the right variables in the right places
-
-  const name = nameParam as 's'
-  const pluralName = pluralParam as 'p'
-  const reducer = reducerCreator.create
-  const reducers: EntityReducers<T, Id, State, 's', 'p'> = {
-    [`addOne${capitalize(name)}` as const]: reducer<T>((state, action) => {
-      adapter.addOne(selectEntityState(state), action)
-    }),
-    [`addMany${capitalize(pluralName)}` as const]: reducer<
-      readonly T[] | Record<Id, T>
-    >((state, action) => {
-      adapter.addMany(selectEntityState(state), action)
-    }),
-    [`setOne${capitalize(name)}` as const]: reducer<T>((state, action) => {
-      adapter.setOne(selectEntityState(state), action)
-    }),
-    [`setMany${capitalize(pluralName)}` as const]: reducer<
-      readonly T[] | Record<Id, T>
-    >((state, action) => {
-      adapter.setMany(selectEntityState(state), action)
-    }),
-    [`setAll${capitalize(pluralName)}` as const]: reducer<
-      readonly T[] | Record<Id, T>
-    >((state, action) => {
-      adapter.setAll(selectEntityState(state), action)
-    }),
-    [`removeOne${capitalize(name)}` as const]: reducer<Id>((state, action) => {
-      adapter.removeOne(selectEntityState(state), action)
-    }),
-    [`removeMany${capitalize(pluralName)}` as const]: reducer<readonly Id[]>(
-      (state, action) => {
-        adapter.removeMany(selectEntityState(state), action)
-      },
-    ),
-    [`removeAll${capitalize(pluralName)}` as const]: reducer((state) => {
-      adapter.removeAll(selectEntityState(state))
-    }),
-    [`upsertOne${capitalize(name)}` as const]: reducer<T>((state, action) => {
-      adapter.upsertOne(selectEntityState(state), action)
-    }),
-    [`upsertMany${capitalize(pluralName)}` as const]: reducer<
-      readonly T[] | Record<Id, T>
-    >((state, action) => {
-      adapter.upsertMany(selectEntityState(state), action)
-    }),
-    [`updateOne${capitalize(name)}` as const]: reducer<Update<T, Id>>(
-      (state, action) => {
-        adapter.updateOne(selectEntityState(state), action)
-      },
-    ),
-    [`updateMany${capitalize(pluralName)}` as const]: reducer<
-      readonly Update<T, Id>[]
-    >((state, action) => {
-      adapter.updateMany(selectEntityState(state), action)
-    }),
-  }
-  return reducers as any
-}
-
-export const entityMethodsCreator: ReducerCreator<
-  typeof entityMethodsCreatorType
-> = {
-  type: entityMethodsCreatorType,
-  create: createEntityMethods,
-}
diff --git a/packages/toolkit/src/entities/state_selectors.ts b/packages/toolkit/src/entities/state_selectors.ts
index eb425aa223..bd64cf001b 100644
--- a/packages/toolkit/src/entities/state_selectors.ts
+++ b/packages/toolkit/src/entities/state_selectors.ts
@@ -1,12 +1,6 @@
 import type { CreateSelectorFunction, Selector, createSelector } from 'reselect'
 import { createDraftSafeSelector } from '../createDraftSafeSelector'
-import type {
-  EntityState,
-  EntitySelectors,
-  EntityId,
-  DefaultPlural,
-} from './models'
-import { capitalize } from './utils'
+import type { EntityState, EntitySelectors, EntityId } from './models'
 
 type AnyFunction = (...args: any) => any
 type AnyCreateSelectorFunction = CreateSelectorFunction<
@@ -14,43 +8,25 @@ type AnyCreateSelectorFunction = CreateSelectorFunction<
   <F extends AnyFunction>(f: F) => F
 >
 
-export interface GetSelectorsOptions<
-  Single extends string = '',
-  Plural extends string = DefaultPlural<''>,
-> {
+export interface GetSelectorsOptions {
   createSelector?: AnyCreateSelectorFunction
-  name?: Single
-  pluralName?: Plural
 }
 
 export function createSelectorsFactory<T, Id extends EntityId>() {
-  function getSelectors<
-    Single extends string = '',
-    Plural extends string = DefaultPlural<Single>,
-  >(
+  function getSelectors(
     selectState?: undefined,
-    options?: GetSelectorsOptions<Single, Plural>,
-  ): EntitySelectors<T, EntityState<T, Id>, Id, Single, Plural>
-  function getSelectors<
-    V,
-    Single extends string = '',
-    Plural extends string = DefaultPlural<Single>,
-  >(
+    options?: GetSelectorsOptions,
+  ): EntitySelectors<T, EntityState<T, Id>, Id>
+  function getSelectors<V>(
     selectState: (state: V) => EntityState<T, Id>,
-    options?: GetSelectorsOptions<Single, Plural>,
-  ): EntitySelectors<T, V, Id, Single, Plural>
-  function getSelectors<
-    V,
-    Single extends string = '',
-    Plural extends string = DefaultPlural<Single>,
-  >(
+    options?: GetSelectorsOptions,
+  ): EntitySelectors<T, V, Id>
+  function getSelectors<V>(
     selectState?: (state: V) => EntityState<T, Id>,
-    options: GetSelectorsOptions<Single, Plural> = {},
-  ): EntitySelectors<T, any, Id, Single, Plural> {
+    options: GetSelectorsOptions = {},
+  ): EntitySelectors<T, any, Id> {
     const {
       createSelector = createDraftSafeSelector as AnyCreateSelectorFunction,
-      name = '',
-      pluralName = name && `${name}s`,
     } = options
 
     const selectIds = (state: EntityState<T, Id>) => state.ids
@@ -69,25 +45,14 @@ export function createSelectorsFactory<T, Id extends EntityId>() {
 
     const selectTotal = createSelector(selectIds, (ids) => ids.length)
 
-    // template literal computed keys don't keep their type if there's an unresolved generic
-    // so we cast to some intermediate type to at least check we're using the right variables in the right places
-
-    const single = name as 's'
-    const plural = pluralName as 'p'
-
     if (!selectState) {
-      const selectors: EntitySelectors<T, any, Id, 's', 'p'> = {
-        [`select${capitalize(single)}Ids` as const]: selectIds,
-        [`select${capitalize(single)}Entities` as const]: selectEntities,
-        [`selectAll${capitalize(plural)}` as const]: selectAll,
-        [`selectTotal${capitalize(plural)}` as const]: selectTotal,
-        [`select${capitalize(single)}ById` as const]: createSelector(
-          selectEntities,
-          selectId,
-          selectById,
-        ),
+      return {
+        selectIds,
+        selectEntities,
+        selectAll,
+        selectTotal,
+        selectById: createSelector(selectEntities, selectId, selectById),
       }
-      return selectors as any
     }
 
     const selectGlobalizedEntities = createSelector(
@@ -95,28 +60,17 @@ export function createSelectorsFactory<T, Id extends EntityId>() {
       selectEntities,
     )
 
-    const selectors: EntitySelectors<T, any, Id, 's', 'p'> = {
-      [`select${capitalize(single)}Ids` as const]: createSelector(
-        selectState,
-        selectIds,
-      ),
-      [`select${capitalize(single)}Entities` as const]:
-        selectGlobalizedEntities,
-      [`selectAll${capitalize(plural)}` as const]: createSelector(
-        selectState,
-        selectAll,
-      ),
-      [`selectTotal${capitalize(plural)}` as const]: createSelector(
-        selectState,
-        selectTotal,
-      ),
-      [`select${capitalize(single)}ById` as const]: createSelector(
+    return {
+      selectIds: createSelector(selectState, selectIds),
+      selectEntities: selectGlobalizedEntities,
+      selectAll: createSelector(selectState, selectAll),
+      selectTotal: createSelector(selectState, selectTotal),
+      selectById: createSelector(
         selectGlobalizedEntities,
         selectId,
         selectById,
       ),
     }
-    return selectors as any
   }
 
   return { getSelectors }
diff --git a/packages/toolkit/src/entities/tests/entity_slice_enhancer.test-d.ts b/packages/toolkit/src/entities/tests/entity_slice_enhancer.test-d.ts
deleted file mode 100644
index 29eca94da9..0000000000
--- a/packages/toolkit/src/entities/tests/entity_slice_enhancer.test-d.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-import type { PayloadActionCreator } from '../../createAction'
-import {
-  buildCreateSlice,
-  createEntityAdapter,
-  entityMethodsCreator,
-  createEntityMethods,
-} from '@reduxjs/toolkit'
-import type { BookModel } from './fixtures/book'
-
-describe('entity slice creator', () => {
-  const createAppSlice = buildCreateSlice({
-    creators: { entityMethods: entityMethodsCreator },
-  })
-  it('should require selectEntityState if state is not compatible', () => {
-    const bookAdapter = createEntityAdapter<BookModel>()
-    const bookSlice = createAppSlice({
-      name: 'books',
-      initialState: { data: bookAdapter.getInitialState() },
-      reducers: (create) => ({
-        // @ts-expect-error
-        ...create.entityMethods(bookAdapter),
-        // @ts-expect-error
-        ...create.entityMethods(bookAdapter, {}),
-        ...create.entityMethods(bookAdapter, {
-          selectEntityState: (state) => state.data,
-        }),
-      }),
-    })
-    expectTypeOf(bookSlice.actions.addOne).toEqualTypeOf<
-      PayloadActionCreator<BookModel, 'books/addOne'>
-    >()
-  })
-  it('exports createEntityMethods which can be used in object form', () => {
-    const bookAdapter = createEntityAdapter<BookModel>()
-
-    const initialState = { data: bookAdapter.getInitialState() }
-
-    const bookSlice = createAppSlice({
-      name: 'books',
-      initialState: { data: bookAdapter.getInitialState() },
-      // @ts-expect-error
-      reducers: {
-        ...createEntityMethods(bookAdapter),
-      },
-    })
-
-    const bookSlice2 = createAppSlice({
-      name: 'books',
-      initialState,
-      reducers: {
-        ...entityMethodsCreator.create(bookAdapter, {
-          // cannot be inferred, needs annotation
-          selectEntityState: (state: typeof initialState) => state.data,
-        }),
-      },
-    })
-
-    expectTypeOf(bookSlice2.actions.addOne).toEqualTypeOf<
-      PayloadActionCreator<BookModel, 'books/addOne'>
-    >()
-
-    const bookSlice3 = createAppSlice({
-      name: 'books',
-      initialState: bookAdapter.getInitialState(),
-      reducers: {
-        ...createEntityMethods(bookAdapter),
-      },
-    })
-
-    expectTypeOf(bookSlice3.actions.addOne).toEqualTypeOf<
-      PayloadActionCreator<BookModel, 'books/addOne'>
-    >()
-  })
-})
diff --git a/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts b/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts
index af5cab2638..6f88bf1aa5 100644
--- a/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts
+++ b/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts
@@ -1,10 +1,4 @@
-import {
-  buildCreateSlice,
-  createEntityAdapter,
-  createSlice,
-  entityMethodsCreator,
-  createEntityMethods,
-} from '@reduxjs/toolkit'
+import { createEntityAdapter, createSlice } from '@reduxjs/toolkit'
 import type {
   PayloadAction,
   SliceCaseReducers,
@@ -60,93 +54,3 @@ function entitySliceEnhancer<
     },
   })
 }
-
-describe('entity slice creator', () => {
-  const createAppSlice = buildCreateSlice({
-    creators: { entityMethods: entityMethodsCreator },
-  })
-
-  const bookAdapter = createEntityAdapter<BookModel>()
-
-  const bookSlice = createAppSlice({
-    name: 'book',
-    initialState: bookAdapter.getInitialState({
-      nested: bookAdapter.getInitialState(),
-    }),
-    reducers: (create) => ({
-      ...create.entityMethods(bookAdapter, {
-        name: 'book',
-      }),
-      ...create.entityMethods(bookAdapter, {
-        selectEntityState: (state) => state.nested,
-        name: 'nestedBook',
-        pluralName: 'nestedBookies',
-      }),
-    }),
-  })
-
-  it('should generate correct actions', () => {
-    expect(bookSlice.actions.addOneBook).toBeTypeOf('function')
-    expect(bookSlice.actions.addOneNestedBook).toBeTypeOf('function')
-    expect(bookSlice.actions.addManyBooks).toBeTypeOf('function')
-    expect(bookSlice.actions.addManyNestedBookies).toBeTypeOf('function')
-  })
-  it('should handle actions', () => {
-    const withBook = bookSlice.reducer(
-      undefined,
-      bookSlice.actions.addOneBook(AClockworkOrange),
-    )
-    expect(
-      bookAdapter.getSelectors().selectById(withBook, AClockworkOrange.id),
-    ).toBe(AClockworkOrange)
-
-    const withNestedBook = bookSlice.reducer(
-      withBook,
-      bookSlice.actions.addOneNestedBook(AClockworkOrange),
-    )
-    expect(
-      bookAdapter
-        .getSelectors(
-          (state: ReturnType<typeof bookSlice.reducer>) => state.nested,
-        )
-        .selectById(withNestedBook, AClockworkOrange.id),
-    ).toBe(AClockworkOrange)
-  })
-  it('should be able to be called without this context', () => {
-    const bookSlice = createAppSlice({
-      name: 'book',
-      initialState: bookAdapter.getInitialState(),
-      reducers: ({ entityMethods }) => ({
-        ...entityMethods(bookAdapter),
-      }),
-    })
-    expect(bookSlice.actions.addOne).toBeTypeOf('function')
-  })
-  it('can be called with object syntax', () => {
-    const bookSlice = createAppSlice({
-      name: 'book',
-      initialState: bookAdapter.getInitialState(),
-      reducers: {
-        ...createEntityMethods(bookAdapter, {
-          name: 'book',
-        }),
-      },
-    })
-    expect(bookSlice.actions.addOneBook).toBeTypeOf('function')
-
-    const initialState = { nested: bookAdapter.getInitialState() }
-    const nestedBookSlice = createAppSlice({
-      name: 'book',
-      initialState,
-      reducers: {
-        ...createEntityMethods(bookAdapter, {
-          // state can't be inferred, so needs to be annotated
-          selectEntityState: (state: typeof initialState) => state.nested,
-          name: 'nestedBook',
-          pluralName: 'nestedBookies',
-        }),
-      },
-    })
-    expect(nestedBookSlice.actions.addOneNestedBook).toBeTypeOf('function')
-  })
-})
diff --git a/packages/toolkit/src/entities/tests/state_selectors.test.ts b/packages/toolkit/src/entities/tests/state_selectors.test.ts
index ef7a48e9eb..dba56cc01d 100644
--- a/packages/toolkit/src/entities/tests/state_selectors.test.ts
+++ b/packages/toolkit/src/entities/tests/state_selectors.test.ts
@@ -147,61 +147,6 @@ describe('Entity State Selectors', () => {
       memoizeSpy.mockClear()
     })
   })
-  describe('named selectors', () => {
-    interface State {
-      books: EntityState<BookModel, string>
-    }
-
-    let adapter: EntityAdapter<BookModel, string>
-    let state: State
-
-    beforeEach(() => {
-      adapter = createEntityAdapter({
-        selectId: (book: BookModel) => book.id,
-      })
-
-      state = {
-        books: adapter.setAll(adapter.getInitialState(), [
-          AClockworkOrange,
-          AnimalFarm,
-          TheGreatGatsby,
-        ]),
-      }
-    })
-    it('should use the provided name and pluralName', () => {
-      const selectors = adapter.getSelectors(undefined, {
-        name: 'book',
-      })
-
-      expect(selectors.selectAllBooks).toBeTypeOf('function')
-      expect(selectors.selectTotalBooks).toBeTypeOf('function')
-      expect(selectors.selectBookById).toBeTypeOf('function')
-
-      expect(selectors.selectAllBooks(state.books)).toEqual([
-        AClockworkOrange,
-        AnimalFarm,
-        TheGreatGatsby,
-      ])
-      expect(selectors.selectTotalBooks(state.books)).toEqual(3)
-    })
-    it('should use the plural of the provided name', () => {
-      const selectors = adapter.getSelectors((state: State) => state.books, {
-        name: 'book',
-        pluralName: 'bookies',
-      })
-
-      expect(selectors.selectAllBookies).toBeTypeOf('function')
-      expect(selectors.selectTotalBookies).toBeTypeOf('function')
-      expect(selectors.selectBookById).toBeTypeOf('function')
-
-      expect(selectors.selectAllBookies(state)).toEqual([
-        AClockworkOrange,
-        AnimalFarm,
-        TheGreatGatsby,
-      ])
-      expect(selectors.selectTotalBookies(state)).toEqual(3)
-    })
-  })
 })
 
 function expectType<T>(t: T) {
diff --git a/packages/toolkit/src/index.ts b/packages/toolkit/src/index.ts
index 51e044a433..678677f7f8 100644
--- a/packages/toolkit/src/index.ts
+++ b/packages/toolkit/src/index.ts
@@ -132,11 +132,6 @@ export type {
   IdSelector,
   Comparer,
 } from './entities/models'
-export {
-  createEntityMethods,
-  entityMethodsCreator,
-  entityMethodsCreatorType,
-} from './entities/slice_creator'
 
 export {
   createAsyncThunk,

From 82c92627e3a744f40c3b5ad5e194192045af852c Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sun, 18 Feb 2024 23:54:38 +0000
Subject: [PATCH 130/178] reset more entity files

---
 .../tests/entity_slice_enhancer.test.ts       | 41 ++++++++++---------
 .../src/entities/tests/entity_state.test.ts   |  4 +-
 .../src/entities/tests/state_adapter.test.ts  |  4 +-
 .../entities/tests/state_selectors.test.ts    |  4 +-
 packages/toolkit/src/entities/utils.ts        |  4 --
 5 files changed, 28 insertions(+), 29 deletions(-)

diff --git a/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts b/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts
index 6f88bf1aa5..0842a20505 100644
--- a/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts
+++ b/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts
@@ -1,44 +1,47 @@
-import { createEntityAdapter, createSlice } from '@reduxjs/toolkit'
+import { createEntityAdapter, createSlice } from '../..'
 import type {
   PayloadAction,
+  Slice,
   SliceCaseReducers,
-  ValidateSliceCaseReducers,
+  UnknownAction,
 } from '../..'
 import type { EntityId, EntityState, IdSelector } from '../models'
-import { AClockworkOrange, type BookModel } from './fixtures/book'
+import type { BookModel } from './fixtures/book'
 
 describe('Entity Slice Enhancer', () => {
-  let slice: ReturnType<typeof entitySliceEnhancer<BookModel, string>>
+  let slice: Slice<EntityState<BookModel, BookModel['id']>>
 
   beforeEach(() => {
-    slice = entitySliceEnhancer({
+    const indieSlice = entitySliceEnhancer({
       name: 'book',
       selectId: (book: BookModel) => book.id,
     })
+    slice = indieSlice
   })
 
   it('exposes oneAdded', () => {
-    const action = slice.actions.oneAdded(AClockworkOrange)
-    const oneAdded = slice.reducer(undefined, action)
-    expect(oneAdded.entities[AClockworkOrange.id]).toBe(AClockworkOrange)
+    const book = {
+      id: '0',
+      title: 'Der Steppenwolf',
+      author: 'Herman Hesse',
+    }
+    const action = slice.actions.oneAdded(book)
+    const oneAdded = slice.reducer(undefined, action as UnknownAction)
+    expect(oneAdded.entities['0']).toBe(book)
   })
 })
 
-interface EntitySliceArgs<
-  T,
-  Id extends EntityId,
-  CaseReducers extends SliceCaseReducers<EntityState<T, Id>>,
-> {
+interface EntitySliceArgs<T, Id extends EntityId> {
   name: string
   selectId: IdSelector<T, Id>
-  modelReducer?: ValidateSliceCaseReducers<EntityState<T, Id>, CaseReducers>
+  modelReducer?: SliceCaseReducers<T>
 }
 
-function entitySliceEnhancer<
-  T,
-  Id extends EntityId,
-  CaseReducers extends SliceCaseReducers<EntityState<T, Id>> = {},
->({ name, selectId, modelReducer }: EntitySliceArgs<T, Id, CaseReducers>) {
+function entitySliceEnhancer<T, Id extends EntityId>({
+  name,
+  selectId,
+  modelReducer,
+}: EntitySliceArgs<T, Id>) {
   const modelAdapter = createEntityAdapter({
     selectId,
   })
diff --git a/packages/toolkit/src/entities/tests/entity_state.test.ts b/packages/toolkit/src/entities/tests/entity_state.test.ts
index b2473f1124..999ee502b9 100644
--- a/packages/toolkit/src/entities/tests/entity_state.test.ts
+++ b/packages/toolkit/src/entities/tests/entity_state.test.ts
@@ -1,5 +1,5 @@
-import type { EntityAdapter } from '../models'
-import { createEntityAdapter } from '../create_adapter'
+import type { EntityAdapter } from '../index'
+import { createEntityAdapter } from '../index'
 import type { PayloadAction } from '../../createAction'
 import { createAction } from '../../createAction'
 import { createSlice } from '../../createSlice'
diff --git a/packages/toolkit/src/entities/tests/state_adapter.test.ts b/packages/toolkit/src/entities/tests/state_adapter.test.ts
index f691639b23..a05b715ff9 100644
--- a/packages/toolkit/src/entities/tests/state_adapter.test.ts
+++ b/packages/toolkit/src/entities/tests/state_adapter.test.ts
@@ -1,5 +1,5 @@
-import type { EntityAdapter } from '../models'
-import { createEntityAdapter } from '../create_adapter'
+import type { EntityAdapter } from '../index'
+import { createEntityAdapter } from '../index'
 import type { PayloadAction } from '../../createAction'
 import { configureStore } from '../../configureStore'
 import { createSlice } from '../../createSlice'
diff --git a/packages/toolkit/src/entities/tests/state_selectors.test.ts b/packages/toolkit/src/entities/tests/state_selectors.test.ts
index dba56cc01d..3afba41ac6 100644
--- a/packages/toolkit/src/entities/tests/state_selectors.test.ts
+++ b/packages/toolkit/src/entities/tests/state_selectors.test.ts
@@ -1,6 +1,6 @@
 import { createDraftSafeSelectorCreator } from '../../createDraftSafeSelector'
-import type { EntityAdapter, EntityState } from '../models'
-import { createEntityAdapter } from '../create_adapter'
+import type { EntityAdapter, EntityState } from '../index'
+import { createEntityAdapter } from '../index'
 import type { EntitySelectors } from '../models'
 import type { BookModel } from './fixtures/book'
 import { AClockworkOrange, AnimalFarm, TheGreatGatsby } from './fixtures/book'
diff --git a/packages/toolkit/src/entities/utils.ts b/packages/toolkit/src/entities/utils.ts
index e9c64c8121..5e3fb92fc1 100644
--- a/packages/toolkit/src/entities/utils.ts
+++ b/packages/toolkit/src/entities/utils.ts
@@ -55,7 +55,3 @@ export function splitAddedUpdatedEntities<T, Id extends EntityId>(
   }
   return [added, updated]
 }
-
-export function capitalize<S extends string>(str: S) {
-  return str && (str.replace(str[0], str[0].toUpperCase()) as Capitalize<S>)
-}

From 4db2dcc5c1c7055dd6c2db4ed5c5b6f05e704a76 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sun, 18 Feb 2024 23:56:18 +0000
Subject: [PATCH 131/178] again

---
 packages/toolkit/src/entities/index.ts | 8 ++++++++
 1 file changed, 8 insertions(+)
 create mode 100644 packages/toolkit/src/entities/index.ts

diff --git a/packages/toolkit/src/entities/index.ts b/packages/toolkit/src/entities/index.ts
new file mode 100644
index 0000000000..e258527474
--- /dev/null
+++ b/packages/toolkit/src/entities/index.ts
@@ -0,0 +1,8 @@
+export { createEntityAdapter } from './create_adapter'
+export type {
+  EntityState,
+  EntityAdapter,
+  Update,
+  IdSelector,
+  Comparer,
+} from './models'

From ebfd549f8d316d7da9790f90cb0074da83b42637 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Mon, 19 Feb 2024 00:11:48 +0000
Subject: [PATCH 132/178] fix test

---
 .../src/entities/tests/entity_slice_enhancer.test.ts  | 11 +++--------
 1 file changed, 3 insertions(+), 8 deletions(-)

diff --git a/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts b/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts
index 0842a20505..b99912e4cd 100644
--- a/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts
+++ b/packages/toolkit/src/entities/tests/entity_slice_enhancer.test.ts
@@ -1,15 +1,10 @@
 import { createEntityAdapter, createSlice } from '../..'
-import type {
-  PayloadAction,
-  Slice,
-  SliceCaseReducers,
-  UnknownAction,
-} from '../..'
-import type { EntityId, EntityState, IdSelector } from '../models'
+import type { PayloadAction, SliceCaseReducers, UnknownAction } from '../..'
+import type { EntityId, IdSelector } from '../models'
 import type { BookModel } from './fixtures/book'
 
 describe('Entity Slice Enhancer', () => {
-  let slice: Slice<EntityState<BookModel, BookModel['id']>>
+  let slice: ReturnType<typeof entitySliceEnhancer<BookModel, string>>
 
   beforeEach(() => {
     const indieSlice = entitySliceEnhancer({

From bd737450e0bc638a7b07d09a9f92e68238c3b758 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Mon, 19 Feb 2024 01:06:31 +0000
Subject: [PATCH 133/178] remove ReducerNamesOfType

---
 docs/api/createSlice.mdx            | 107 +++++++++++++++-------------
 packages/toolkit/src/createSlice.ts |   9 ---
 packages/toolkit/src/index.ts       |   1 -
 3 files changed, 58 insertions(+), 59 deletions(-)

diff --git a/docs/api/createSlice.mdx b/docs/api/createSlice.mdx
index 64326aa2e1..206577af88 100644
--- a/docs/api/createSlice.mdx
+++ b/docs/api/createSlice.mdx
@@ -1146,7 +1146,13 @@ It should be an object with some of the following properties:
 
 The actions property will typically be a [mapped type](https://www.typescriptlang.org/docs/handbook/2/mapped-types.html) over the `CaseReducers` type parameter, returning what the creator's `handle` would expose when given that definition.
 
-The `ReducerNamesOfType` utility is exported to easily filter down to reducers that would be passed to the `handle` callback.
+In order to ensure that the definitions are correctly filtered to only include those handled by the creator, a conditional type should be used, typically checking the definition extends a `ReducerDefinition` with the right type.
+
+```ts no-transpile
+{
+  [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends ReducerDefinition<typeof creatorType> ? ActionTypeHere : never
+}
+```
 
 For example, with (a simplified version of) the `asyncThunk` creator:
 
@@ -1166,10 +1172,7 @@ declare module '@reduxjs/toolkit' {
       {
         // highlight-start
         actions: {
-          [ReducerName in ReducerNamesOfType<
-            CaseReducers,
-            typeof asyncThunkCreatorType
-          >]: CaseReducers[ReducerName] extends AsyncThunkReducerDefinition<
+          [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends AsyncThunkReducerDefinition<
             State,
             infer ThunkArg,
             infer Returned
@@ -1206,23 +1209,25 @@ declare module '@reduxjs/toolkit' {
       ) => PreparedCaseReducerDefinition<State, Prepare>,
       {
         actions: {
-          [ReducerName in ReducerNamesOfType<
-            CaseReducers,
+          [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends ReducerDefinition<
             typeof preparedReducerType
-          >]: CaseReducers[ReducerName] extends { prepare: any }
-            ? ActionCreatorForCaseReducerWithPrepare<
-                CaseReducers[ReducerName],
-                SliceActionType<Name, ReducerName>
-              >
+          >
+            ? CaseReducers[ReducerName] extends { prepare: any }
+              ? ActionCreatorForCaseReducerWithPrepare<
+                  CaseReducers[ReducerName],
+                  SliceActionType<Name, ReducerName>
+                >
+              : never
             : never
         }
         // highlight-start
         caseReducers: {
-          [ReducerName in ReducerNamesOfType<
-            CaseReducers,
+          [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends ReducerDefinition<
             typeof preparedReducerType
-          >]: CaseReducers[ReducerName] extends { reducer: infer Reducer }
-            ? Reducer
+          >
+            ? CaseReducers[ReducerName] extends { reducer: infer Reducer }
+              ? Reducer
+              : never
             : never
         }
         // highlight-end
@@ -1276,6 +1281,10 @@ interface ToastReducerConfig<State> {
   hidden?: CaseReducer<State, PayloadAction<undefined, string, { id: string }>>
 }
 
+interface ToastReducerDefinition<State>
+  extends ReducerDefinition<typeof toastCreatorType>,
+    ToastReducerConfig<State> {}
+
 interface ToastThunkCreator<
   SliceName extends string,
   ReducerName extends string,
@@ -1304,20 +1313,17 @@ declare module '@reduxjs/toolkit' {
     Name extends string,
   > {
     [toastCreatorType]: ReducerCreatorEntry<
-      (
-        config: ToastReducerConfig<State>,
-      ) => ToastReducerConfig<State> &
-        ReducerDefinition<typeof toastCreatorType>,
+      (config: ToastReducerConfig<State>) => ToastReducerDefinition<State>,
       {
         actions: {
-          [ReducerName in ReducerNamesOfType<
-            typeof toastCreatorType
-          >]: ToastThunkCreator<Name, ReducerName>
+          [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends ToastReducerDefinition<State>
+            ? ToastThunkCreator<Name, ReducerName>
+            : never
         }
         caseReducers: {
-          [ReducerName in ReducerNamesOfType<
-            typeof toastCreatorType
-          >]: Required<ToastReducerConfig<State>>
+          [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends ToastReducerDefinition<State>
+            ? Required<ToastReducerConfig<State>>
+            : never
         }
       }
     >
@@ -1499,38 +1505,41 @@ interface HistoryState<T> {
 declare module '@reduxjs/toolkit' {
   export interface SliceReducerCreators<
     State,
-    CaseReducers extends
-      CreatorCaseReducers<State>,
+    CaseReducers extends CreatorCaseReducers<State>,
     Name extends string,
   > {
-    [paginationCreatorType]: ReducerCreatorEntry<
+    [historyCreatorType]: ReducerCreatorEntry<
       // make sure the creator is only called when state is compatibleState extends HistoryState<unknown>
-        ?
-      (this: ReducerCreators<State>) => {
+      State extends HistoryState<any>
+        ? (this: ReducerCreators<State>) => {
             undo: CaseReducerDefinition<State, PayloadAction>
             redo: CaseReducerDefinition<State, PayloadAction>
-            reset: ReducerDefinition<typeof paginationCreatorType> & {
+            reset: ReducerDefinition<typeof historyCreatorType> & {
               type: 'reset'
             }
           }
-        : never, {
-      actions: {
-        [ReducerName in ReducerNamesOfType<
-          CaseReducers,
-          typeof historyMethodsCreatorType
-        >]: CaseReducers[ReducerName] extends { type: 'reset' }
-          ? PayloadActionCreator<void, SliceActionType<Name, ReducerName>>
-          : never
-      }
-      caseReducers: {
-        [ReducerName in ReducerNamesOfType<
-          CaseReducers,
-          typeof historyMethodsCreatorType
-        >]: CaseReducers[ReducerName] extends { type: 'reset' }
-          ? CaseReducer<State, PayloadAction>
-          : never
+        : never,
+      {
+        actions: {
+          [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends ReducerDefinition<
+            typeof historyCreatorType
+          >
+            ? CaseReducers[ReducerName] extends { type: 'reset' }
+              ? PayloadActionCreator<void, SliceActionType<Name, ReducerName>>
+              : never
+            : never
+        }
+        caseReducers: {
+          [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends ReducerDefinition<
+            typeof historyCreatorType
+          >
+            ? CaseReducers[ReducerName] extends { type: 'reset' }
+              ? CaseReducer<State, PayloadAction>
+              : never
+            : never
+        }
       }
-    }>
+    >
   }
 }
 
diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index d133262c01..75d2a217b6 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -321,15 +321,6 @@ export type ReducerCreator<Type extends RegisteredReducerType> = {
       ): void
     })
 
-export type ReducerNamesOfType<
-  CaseReducers extends CreatorCaseReducers<any>,
-  Type extends RegisteredReducerType,
-> = {
-  [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends ReducerDefinition<Type>
-    ? ReducerName
-    : never
-}[keyof CaseReducers]
-
 interface InjectIntoConfig<NewReducerPath extends string> extends InjectConfig {
   reducerPath?: NewReducerPath
 }
diff --git a/packages/toolkit/src/index.ts b/packages/toolkit/src/index.ts
index 678677f7f8..32747dcbaa 100644
--- a/packages/toolkit/src/index.ts
+++ b/packages/toolkit/src/index.ts
@@ -84,7 +84,6 @@ export type {
   SliceSelectors,
   SliceReducerCreators,
   ReducerDefinition,
-  ReducerNamesOfType,
   ReducerCreatorEntry,
   ReducerCreator,
   ReducerDetails,

From 82da65d2be274b7568c5f9c2ca6d6a18cbb9dd76 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Mon, 19 Feb 2024 23:57:25 +0000
Subject: [PATCH 134/178] bump version reference

---
 docs/api/createSlice.mdx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/api/createSlice.mdx b/docs/api/createSlice.mdx
index 206577af88..b2ee3982cb 100644
--- a/docs/api/createSlice.mdx
+++ b/docs/api/createSlice.mdx
@@ -847,7 +847,7 @@ reducers: (create) => {
 
 ### Writing your own creators
 
-In version v2.2.0 (TODO), we introduced a system for including your own creators.
+In version v2.3.0, we introduced a system for including your own creators.
 
 The below documentation will cover how to write your own creators, and how to use them with `createSlice`.
 

From 37bd76ed38f4517a6708c5d5833c87aee4383142 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Thu, 22 Feb 2024 21:45:54 +0000
Subject: [PATCH 135/178] add usage page

---
 docs/usage/custom-slice-creators.md | 8 ++++++++
 website/sidebars.json               | 3 ++-
 2 files changed, 10 insertions(+), 1 deletion(-)
 create mode 100644 docs/usage/custom-slice-creators.md

diff --git a/docs/usage/custom-slice-creators.md b/docs/usage/custom-slice-creators.md
new file mode 100644
index 0000000000..99331064da
--- /dev/null
+++ b/docs/usage/custom-slice-creators.md
@@ -0,0 +1,8 @@
+---
+id: custom-slice-creators
+title: Custom Slice Creators
+sidebar_label: Custom Slice Creators
+hide_title: true
+---
+
+# Custom Slice Creators
diff --git a/website/sidebars.json b/website/sidebars.json
index ef79ee1a75..d382ab6977 100644
--- a/website/sidebars.json
+++ b/website/sidebars.json
@@ -34,7 +34,8 @@
         "usage/usage-guide",
         "usage/usage-with-typescript",
         "usage/immer-reducers",
-        "usage/nextjs"
+        "usage/nextjs",
+        "usage/custom-slice-creators"
       ]
     },
     {

From 633028d932db58267afd4fb5a7f0510d16c14adc Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Thu, 22 Feb 2024 22:35:54 +0000
Subject: [PATCH 136/178] move creator callback section back and cut and paste
 slice creators section to usage guide

---
 docs/api/createSlice.mdx             | 1381 ++++----------------------
 docs/usage/custom-slice-creators.md  |    8 -
 docs/usage/custom-slice-creators.mdx | 1201 ++++++++++++++++++++++
 3 files changed, 1376 insertions(+), 1214 deletions(-)
 delete mode 100644 docs/usage/custom-slice-creators.md
 create mode 100644 docs/usage/custom-slice-creators.mdx

diff --git a/docs/api/createSlice.mdx b/docs/api/createSlice.mdx
index 078991fc93..dba5ee9576 100644
--- a/docs/api/createSlice.mdx
+++ b/docs/api/createSlice.mdx
@@ -134,11 +134,11 @@ const todosSlice = createSlice({
 })
 ```
 
-:::tip Slice creators and the `reducers` "creator callback" notation
+### The `reducers` "creator callback" notation
 
-The `reducers` field can also be a callback which receives a "create" object. This allows you to use [slice creators](#slice-creators) with features such as [async thunks](./createAsyncThunk) as part of your slice.
+Alternatively, the `reducers` field can be a callback which receives a "create" object.
 
-This is fully covered in the [slice creators](#slice-creators) section, but here's a quick example:
+The main benefit of this is that you can use [custom slice creators](../usage/custom-slice-creators), such as `asyncThunkCreator` which allows creating [async thunks](./createAsyncThunk) as part of your slice (though you [need a bit of setup for this](#createasyncthunk)). Types are also slightly simplified for prepared reducers.
 
 ```ts title="Creator callback for reducers"
 import { buildCreateSlice, asyncThunkCreator, nanoid } from '@reduxjs/toolkit'
@@ -164,33 +164,195 @@ const todosSlice = createAppSlice({
     todos: [],
   } satisfies TodoState as TodoState,
   reducers: (create) => ({
+    deleteTodo: create.reducer<number>((state, action) => {
+      state.todos.splice(action.payload, 1)
+    }),
+    addTodo: create.preparedReducer(
+      (text: string) => {
+        const id = nanoid()
+        return { payload: { id, text } }
+      },
+      // action type is inferred from prepare callback
+      (state, action) => {
+        state.todos.push(action.payload)
+      },
+    ),
     fetchTodo: create.asyncThunk(
-      // payload creator
       async (id: string, thunkApi) => {
         const res = await fetch(`myApi/todos?id=${id}`)
         return (await res.json()) as Item
       },
       {
-        // reducers for lifecycle
         pending: (state) => {
           state.loading = true
         },
-        fulfilled: (state, action) => {
-          state.todos.push(action.payload)
-        },
-        settled: (state, action) => {
+        rejected: (state, action) => {
           state.loading = false
         },
-        // additional options
-        options: {
-          idGenerator: () => nanoid(30),
+        fulfilled: (state, action) => {
+          state.loading = false
+          state.todos.push(action.payload)
         },
       },
     ),
   }),
 })
 
-export const { fetchTodo } = todosSlice.actions
+export const { addTodo, deleteTodo, fetchTodo } = todosSlice.actions
+```
+
+#### Create Methods
+
+#### `create.reducer`
+
+A standard slice case reducer.
+
+**Parameters**
+
+- **reducer** The slice case reducer to use.
+
+```ts no-transpile
+create.reducer<Todo>((state, action) => {
+  state.todos.push(action.payload)
+})
+```
+
+#### `create.preparedReducer`
+
+A [prepared](#customizing-generated-action-creators) reducer, to customize the action creator.
+
+**Parameters**
+
+- **prepareAction** The [`prepare callback`](./createAction#using-prepare-callbacks-to-customize-action-contents).
+- **reducer** The slice case reducer to use.
+
+The action passed to the case reducer will be inferred from the prepare callback's return.
+
+```ts no-transpile
+create.preparedReducer(
+  (text: string) => {
+    const id = nanoid()
+    return { payload: { id, text } }
+  },
+  (state, action) => {
+    state.todos.push(action.payload)
+  },
+)
+```
+
+#### `create.asyncThunk`
+
+Creates an async thunk instead of an action creator.
+
+:::caution Setup
+
+To avoid pulling `createAsyncThunk` into the bundle size of `createSlice` by default, some extra setup is required to use `create.asyncThunk`.
+
+The version of `createSlice` exported from RTK will not include `create.asyncThunk`.
+
+Instead, import `buildCreateSlice` and `asyncThunkCreator`, and create your own version of `createSlice`:
+
+```ts
+import { buildCreateSlice, asyncThunkCreator } from '@reduxjs/toolkit'
+
+export const createAppSlice = buildCreateSlice({
+  creators: { asyncThunk: asyncThunkCreator },
+})
+```
+
+Then import this `createAppSlice` as needed instead of the exported version from RTK.
+
+This same approach can be used to include other [custom creators](../usage/custom-slice-creators).
+
+:::
+
+**Parameters**
+
+- **payloadCreator** The thunk [payload creator](./createAsyncThunk#payloadcreator).
+- **config** The configuration object. (optional)
+
+The configuration object can contain case reducers for each of the [lifecycle actions](./createAsyncThunk#promise-lifecycle-actions) (`pending`, `fulfilled`, and `rejected`), as well as a `settled` reducer that will run for both fulfilled and rejected actions (note that this will run _after_ any provided `fulfilled`/`rejected` reducers. Conceptually it can be thought of like a `finally` block.).
+
+Each case reducer will be attached to the slice's `caseReducers` object, e.g. `slice.caseReducers.fetchTodo.fulfilled`.
+
+The configuration object can also contain [`options`](./createAsyncThunk#options).
+
+```ts no-transpile
+create.asyncThunk(
+  async (id: string, thunkApi) => {
+    const res = await fetch(`myApi/todos?id=${id}`)
+    return (await res.json()) as Item
+  },
+  {
+    pending: (state) => {
+      state.loading = true
+    },
+    rejected: (state, action) => {
+      state.error = action.payload ?? action.error
+    },
+    fulfilled: (state, action) => {
+      state.todos.push(action.payload)
+    },
+    settled: (state, action) => {
+      state.loading = false
+    }
+    options: {
+      idGenerator: uuid,
+    },
+  }
+)
+```
+
+:::note
+
+Typing for the `create.asyncThunk` works in the same way as [`createAsyncThunk`](../usage/usage-with-typescript#createasyncthunk), with one key difference.
+
+A type for `state` and/or `dispatch` _cannot_ be provided as part of the `ThunkApiConfig`, as this would cause circular types.
+
+Instead, it is necessary to assert the type when needed - `getState() as RootState`. You may also include an explicit return type for the payload function as well, in order to break the circular type inference cycle.
+
+```ts no-transpile
+create.asyncThunk<Todo, string, { rejectValue: { error: string } }>(
+  // highlight-start
+  // may need to include an explicit return type
+  async (id: string, thunkApi): Promise<Todo> => {
+    // Cast types for `getState` and `dispatch` manually
+    const state = thunkApi.getState() as RootState
+    const dispatch = thunkApi.dispatch as AppDispatch
+    // highlight-end
+    try {
+      const todo = await fetchTodo()
+      return todo
+    } catch (e) {
+      throw thunkApi.rejectWithValue({
+        error: 'Oh no!',
+      })
+    }
+  },
+)
+```
+
+For common thunk API configuration options, a [`withTypes` helper](../usage/usage-with-typescript#defining-a-pre-typed-createasyncthunk) is provided:
+
+```ts no-transpile
+reducers: (create) => {
+  const createAThunk = create.asyncThunk.withTypes<{
+    rejectValue: { error: string }
+  }>()
+
+  return {
+    fetchTodo: createAThunk<Todo, string>(async (id, thunkApi) => {
+      throw thunkApi.rejectWithValue({
+        error: 'Oh no!',
+      })
+    }),
+    fetchTodos: createAThunk<Todo[], string>(async (id, thunkApi) => {
+      throw thunkApi.rejectWithValue({
+        error: 'Oh no, not again!',
+      })
+    }),
+  }
+}
 ```
 
 :::
@@ -525,1196 +687,3 @@ function buildCreateSlice({
 ```
 
 It returns an instance of [`createSlice`](#).
-
-## Slice creators
-
-Redux Toolkit 2.0 introduces the concept of "slice creators", which allow you to define reusable logic for creating slices.
-
-These "creators" have the capability to:
-
-- Define multiple reducers at the same time
-- Modify slice behaviour by adding case/matcher reducers
-- Expose custom actions (thunks, for example) and case reducers
-
-```ts no-transpile
-const createAppSlice = buildCreateSlice({
-  creators: { historyMethods: historyCreator, undoable: undoableCreator },
-})
-
-const postSliceWithHistory = createAppSlice({
-  name: 'post',
-  initialState: getInitialHistoryState({ title: '', pinned: false }),
-  reducers: (create) => ({
-    ...create.historyMethods(),
-    updateTitle: create.preparedReducer(
-      create.undoable.withPayload<string>(),
-      create.undoable((state, action) => {
-        state.title = action.payload
-      }),
-    ),
-    togglePinned: create.preparedReducer(
-      create.undoable.withoutPayload(),
-      create.undoable((state, action) => {
-        state.pinned = !state.pinned
-      }),
-    ),
-  }),
-})
-
-const { undo, redo, reset, updateTitle, togglePinned } =
-  postSliceWithHistory.actions
-```
-
-### The `reducers` "creator callback" notation
-
-In order to use slice creators, `reducers` becomes a callback, which receives a `create` object. This `create` object contains a couple of [inbuilt creators](#rtk-creators), along with any creators passed to [`buildCreateSlice`](#buildcreateslice).
-
-```ts title="Creator callback for reducers"
-import { buildCreateSlice, asyncThunkCreator, nanoid } from '@reduxjs/toolkit'
-
-const createAppSlice = buildCreateSlice({
-  creators: { asyncThunk: asyncThunkCreator },
-})
-
-interface Item {
-  id: string
-  text: string
-}
-
-interface TodoState {
-  loading: boolean
-  todos: Item[]
-}
-
-const todosSlice = createAppSlice({
-  name: 'todos',
-  initialState: {
-    loading: false,
-    todos: [],
-  } as TodoState,
-  reducers: (create) => ({
-    deleteTodo: create.reducer<number>((state, action) => {
-      state.todos.splice(action.payload, 1)
-    }),
-    addTodo: create.preparedReducer(
-      (text: string) => {
-        const id = nanoid()
-        return { payload: { id, text } }
-      },
-      // action type is inferred from prepare callback
-      (state, action) => {
-        state.todos.push(action.payload)
-      },
-    ),
-    fetchTodo: create.asyncThunk(
-      async (id: string, thunkApi) => {
-        const res = await fetch(`myApi/todos?id=${id}`)
-        return (await res.json()) as Item
-      },
-      {
-        pending: (state) => {
-          state.loading = true
-        },
-        rejected: (state, action) => {
-          state.loading = false
-        },
-        fulfilled: (state, action) => {
-          state.loading = false
-          state.todos.push(action.payload)
-        },
-      },
-    ),
-  }),
-})
-
-export const { addTodo, deleteTodo, fetchTodo } = todosSlice.actions
-```
-
-#### RTK Creators
-
-These creators come built into RTK, and are always available on the `create` object passed to the `reducers` callback.
-
-##### `create.reducer`
-
-A standard slice case reducer. Creates an action creator with the same name as the reducer.
-
-**Parameters**
-
-- **reducer** The slice case reducer to use.
-
-```ts no-transpile
-create.reducer<Todo>((state, action) => {
-  state.todos.push(action.payload)
-})
-```
-
-:::tip
-
-The [creator definition](#creator-definitions) for `create.reducer` is exported from RTK as `reducerCreator`, to allow reuse.
-
-:::
-
-##### `create.preparedReducer`
-
-A [prepared](#customizing-generated-action-creators) reducer, to customize the action creator. Creates a prepared action creator with the same name as the reducer.
-
-**Parameters**
-
-- **prepareAction** The [`prepare callback`](./createAction#using-prepare-callbacks-to-customize-action-contents).
-- **reducer** The slice case reducer to use.
-
-The action passed to the case reducer will be inferred from the prepare callback's return.
-
-```ts no-transpile
-create.preparedReducer(
-  (text: string) => {
-    const id = nanoid()
-    return { payload: { id, text } }
-  },
-  (state, action) => {
-    state.todos.push(action.payload)
-  },
-)
-```
-
-:::tip
-
-The [creator definition](#creator-definitions) for `create.preparedReducer` is exported from RTK as `preparedReducerCreator`, to allow reuse.
-
-:::
-
-#### Optional RTK Creators
-
-These creators are not included in the default `create` object, but can be added by passing them to [`buildCreateSlice`](#buildcreateslice).
-
-The name the creator is available under is based on the key used when calling `buildCreateSlice`. For example, to use `create.asyncThunk`:
-
-```ts
-import { buildCreateSlice, asyncThunkCreator } from '@reduxjs/toolkit'
-
-export const createAppSlice = buildCreateSlice({
-  creators: {
-    // highlight-next-line
-    asyncThunk: asyncThunkCreator,
-  },
-})
-
-interface Post {
-  id: string
-  text: string
-}
-
-export const postsSlice = createAppSlice({
-  name: 'posts',
-  initialState: [] as Post[],
-  reducers: (create) => ({
-    // highlight-next-line
-    fetchPosts: create.asyncThunk(
-      async () => {
-        const res = await fetch('myApi/posts')
-        return (await res.json()) as Post[]
-      },
-      {
-        fulfilled(state, action) {
-          return action.payload
-        },
-      },
-    ),
-  }),
-})
-```
-
-For clarity these docs will use recommended names.
-
-:::tip
-
-We recommend using `createAppSlice` consistently throughout your app as a replacement for `createSlice`.
-
-This avoids having to consider whether the creators are needed for each slice.
-
-:::
-
-:::caution
-
-To avoid collision, names used by [RTK creators](#rtk-creators) are reserved - passing creators under the `reducer` or `preparedReducer` keys is not allowed, and only `asyncThunkCreator` is allowed to be passed under the `asyncThunk` key.
-
-```ts no-transpile
-const createAppSlice = buildCreateSlice({
-  creators: {
-    reducer: aCustomCreator, // not allowed, name is reserved
-    asyncThunk: aCustomCreator, // not allowed, must be asyncThunkCreator
-    asyncThunk: asyncThunkCreator, // allowed
-  },
-})
-```
-
-:::
-
-##### `create.asyncThunk` (`asyncThunkCreator`)
-
-Creates an async thunk and adds any provided case reducers for lifecycle actions.
-
-**Parameters**
-
-- **payloadCreator** The thunk [payload creator](./createAsyncThunk#payloadcreator).
-- **config** The configuration object. (optional)
-
-The configuration object can contain case reducers for each of the [lifecycle actions](./createAsyncThunk#promise-lifecycle-actions) (`pending`, `fulfilled`, and `rejected`), as well as a `settled` reducer that will run for both fulfilled and rejected actions (note that this will run _after_ any provided `fulfilled`/`rejected` reducers. Conceptually it can be thought of like a `finally` block.).
-
-Each case reducer will be attached to the slice's `caseReducers` object, e.g. `slice.caseReducers.fetchTodo.fulfilled`.
-
-The configuration object can also contain [`options`](./createAsyncThunk#options).
-
-```ts no-transpile
-create.asyncThunk(
-  async (id: string, thunkApi) => {
-    const res = await fetch(`myApi/todos?id=${id}`)
-    return (await res.json()) as Item
-  },
-  {
-    pending: (state) => {
-      state.loading = true
-    },
-    rejected: (state, action) => {
-      state.error = action.payload ?? action.error
-    },
-    fulfilled: (state, action) => {
-      state.todos.push(action.payload)
-    },
-    settled: (state, action) => {
-      state.loading = false
-    }
-    options: {
-      idGenerator: uuid,
-    },
-  }
-)
-```
-
-:::note
-
-Typing for `create.asyncThunk` works in the same way as [`createAsyncThunk`](../usage/usage-with-typescript#createasyncthunk), with one key difference.
-
-A type for `state` and/or `dispatch` _cannot_ be provided as part of the `ThunkApiConfig`, as this would cause circular types.
-
-Instead, it is necessary to assert the type when needed - `getState() as RootState`. You may also include an explicit return type for the payload function as well, in order to break the circular type inference cycle.
-
-```ts no-transpile
-create.asyncThunk<Todo, string, { rejectValue: { error: string } }>(
-  // highlight-start
-  // may need to include an explicit return type
-  async (id: string, thunkApi): Promise<Todo> => {
-    // Cast types for `getState` and `dispatch` manually
-    const state = thunkApi.getState() as RootState
-    const dispatch = thunkApi.dispatch as AppDispatch
-    // highlight-end
-    try {
-      const todo = await fetchTodo()
-      return todo
-    } catch (e) {
-      throw thunkApi.rejectWithValue({
-        error: 'Oh no!',
-      })
-    }
-  },
-)
-```
-
-For common thunk API configuration options, a [`withTypes` helper](../usage/usage-with-typescript#defining-a-pre-typed-createasyncthunk) is provided:
-
-```ts no-transpile
-reducers: (create) => {
-  const createAThunk = create.asyncThunk.withTypes<{
-    rejectValue: { error: string }
-  }>()
-
-  return {
-    fetchTodo: createAThunk<Todo, string>(async (id, thunkApi) => {
-      throw thunkApi.rejectWithValue({
-        error: 'Oh no!',
-      })
-    }),
-    fetchTodos: createAThunk<Todo[], string>(async (id, thunkApi) => {
-      throw thunkApi.rejectWithValue({
-        error: 'Oh no, not again!',
-      })
-    }),
-  }
-}
-```
-
-:::
-
-### Writing your own creators
-
-In version v2.3.0, we introduced a system for including your own creators.
-
-The below documentation will cover how to write your own creators, and how to use them with `createSlice`.
-
-#### Reducer definitions
-
-A reducer definition is an object (or function) with a `_reducerDefinitionType` property indicating which creator should handle it. Other than this property, it is entirely up to the creator what this definition object can look like.
-
-For example, the `create.preparedReducer` creator uses a definition that looks like `{ prepare, reducer }`.
-
-The callback form of `reducers` should return an object of reducer definitions, by calling creators and nesting the result of each under a key.
-
-```js no-transpile
-reducers: (create) => ({
-  addTodo: create.preparedReducer(
-    (todo) => ({ payload: { id: nanoid(), ...todo } }),
-    (state, action) => {
-      state.push(action.payload)
-    },
-  ),
-})
-// becomes
-const definitions = {
-  addTodo: {
-    _reducerDefinitionType: 'reducerWithPrepare',
-    prepare: (todo) => ({ payload: { id: nanoid(), ...todo } }),
-    reducer: (state, action) => {
-      state.push(action.payload)
-    },
-  },
-}
-```
-
-Typically a creator will return a [single reducer definition](#single-definitions), but it could return an object of [multiple definitions](#multiple-definitions) to be spread into the final object, or [something else entirely](#other)!
-
-#### Creator definitions
-
-A creator definition contains the actual runtime logic for that creator. It's an object with a `type` property, a `create` method, and an optional `handle` method.
-
-It's passed to [`buildCreateSlice`](#buildcreateslice) as part of the `creators` object, and the name used when calling `buildCreateSlice` will be the key the creator is nested under in the `create` object.
-
-```ts no-transpile
-import { buildCreateSlice } from '@reduxjs/toolkit'
-
-const createAppSlice = buildCreateSlice({
-  // highlight-next-line
-  creators: { batchable: batchableCreator },
-})
-
-const todoSlice = createSlice({
-  name: 'todos',
-  initialState: [] as Todo[],
-  reducers: (create) => ({
-    // highlight-next-line
-    addTodo: create.batchable<Todo>((state, action) => {
-      state.push(action.payload)
-    }),
-  }),
-})
-```
-
-The `type` property of the definition should be the same constant used for reducer definitions to be handled by this creator. To avoid collision, we recommend using Symbols for this. It's also used for defining/retrieving types - see [Typescript](#typescript).
-
-```ts no-transpile
-const reducerCreatorType = Symbol()
-
-const reducerCreator: ReducerCreator<typeof reducerCreatorType> = {
-  type: reducerCreatorType,
-  create(reducer) {
-    return {
-      _reducerDefinitionType: reducerCreatorType,
-      reducer,
-    }
-  },
-  handle({ reducerName, type }, definition, context) {
-    const { reducer } = definition
-    const actionCreator = createAction(type)
-    context
-      .addCase(actionCreator, reducer)
-      .exposeAction(reducerName, actionCreator)
-      .exposeCaseReducer(reducerName, reducer)
-  },
-}
-```
-
-##### `create`
-
-The `create` method is the function that will be attached to the `create` object, before it's passed to the `reducers` callback.
-
-Because it's a function, the `this` value will be the final `create` object when called (assuming a `create.creator()` call). It also could have additional methods attached.
-
-See the [Further examples](#further-examples) section for some examples of these.
-
-#### `handle`
-
-The `handle` callback of a creator will be called for any reducer definitions with a matching `_reducerDefinitionType` property.
-
-:::note
-A creator only needs a `handle` callback if it expects to be called with reducer definitions. If it only calls other creators (see [Using `this` to access other creators](#create-1)), it can omit the `handle`.
-:::
-
-It receives three arguments: details about the reducer, the definition, and a `context` object with methods to modify the slice.
-
-The reducer details object has two properties:
-
-- `reducerName` - the key the reducer definition was under (e.g. `addTodo`)
-- `type` - the automatically generated type string for the reducer (e.g. `todos/addTodo`)
-
-The context object includes:
-
-##### `addCase`
-
-The same as [`addCase`](./createReducer#builderaddcase) for `createReducer` and `extraReducers`. Adds a case reducer for a given action type, and can receive an action type string or an action creator with a `.type` property.
-
-```ts no-transpile
-const action = createAction(type)
-context.addCase(action, reducer)
-```
-
-##### `addMatcher`
-
-The same as [`addMatcher`](./createReducer#builderaddmatcher) for `createReducer` and `extraReducers`. Adds a case reducer which will be called when a given matcher returns true.
-
-```ts no-transpile
-const matcher = isAnyOf(action, action2)
-context.addMatcher(matcher, reducer)
-```
-
-##### `exposeAction`
-
-Attaches a value to `slice.actions`. Receives the key to be set under (typically `reducerName`) and the value to be set.
-
-```ts no-transpile
-const action = createAction(type)
-context.exposeAction(reducerName, action)
-```
-
-##### `exposeCaseReducer`
-
-Attaches a value to `slice.caseReducers`. Receives the key to be set under (typically `reducerName`) and the value to be set.
-
-```ts no-transpile
-context.exposeCaseReducer(reducerName, reducer)
-```
-
-##### `getInitialState`
-
-Returns the initial state value for the slice. If a lazy state initializer has been provided, it will be called and a fresh value returned.
-
-```ts no-transpile
-const resetAction = createAction(type)
-const resetReducer = () => context.getInitialState()
-context
-  .addCase(resetAction, resetReducer)
-  .exposeAction(reducerName, resetAction)
-  .exposeCaseReducer(reducerName, resetReducer)
-```
-
-#### Typescript
-
-The Typescript system for custom slice creators uses a "creator registry" system similar to the module system for [RTK Query](/rtk-query/usage/customizing-create-api#creating-your-own-module).
-
-Creators are registered by using module augmentation to add a new key (their unique `type`) to the `SliceReducerCreators` interface. The interface receives three type parameters (`State`, `CaseReducers` and `Name`), and each entry should use the `ReducerCreatorEntry` type utility.
-
-```ts no-transpile
-const reducerCreatorType = Symbol()
-
-declare module '@reduxjs/toolkit' {
-  export interface SliceReducerCreators<
-    State,
-    CaseReducers extends CreatorCaseReducers<State>,
-    Name extends string,
-  > {
-    [reducerCreatorType]: ReducerCreatorEntry<
-      () => ReducerDefinition<typeof reducerCreatorType>
-    >
-  }
-}
-```
-
-The type parameters for `SliceReducerCreators` are:
-
-- `State` - The state type used by the slice.
-- `CaseReducers` - The case reducer definitions returned by the creator callback.
-- `Name` - The [`name`](#name) used by the slice.
-
-The `ReducerCreatorEntry<Create, Exposes>` utility has two type parameters:
-
-##### `Create`
-
-The signature of the `create` method of the creator definition.
-
-:::caution `CaseReducers` and `Name`
-
-Your `Create` type should not depend on the `CaseReducers` and `Name` type parameters, as these will not yet exist when the creator is being called.
-
-:::
-
-:::tip Using `this` to access other creators
-
-Assuming the creator is called as `create.yourCreator()`, the `this` value for the function is the `create` object - meaning you can call other creators on the same object.
-
-However, this should be specifically included in the function signature, so Typescript can warn if called with an incorrect context (for example, if the user destructures from the `create` value).
-
-```ts no-transpile
-const batchedCreatorType = Symbol()
-
-declare module '@reduxjs/toolkit' {
-  export interface SliceReducerCreators<
-    State,
-    CaseReducers extends CreatorCaseReducers<State>,
-    Name extends string,
-  > {
-    [batchedCreatorType]: ReducerCreatorEntry<
-      <Payload>(
-        // highlight-next-line
-        this: ReducerCreators<State, {}>,
-        reducer: CaseReducer<State, PayloadAction<Payload>>,
-      ) => PreparedCaseReducerDefinition<
-        State,
-        (payload: Payload) => { payload: Payload; meta: unknown }
-      >
-    >
-  }
-}
-
-const batchedCreator: ReducerCreator<typeof batchedCreatorType> = {
-  type: batchedCreatorType,
-  create(reducer) {
-    return this.preparedReducer(prepareAutoBatched(), reducer)
-  },
-}
-```
-
-The second argument to the `ReducerCreators` type is a map from creator names to types, which you should supply if you're expecting to use any custom creators (anything other than `reducer` and `preparedReducer`) within your own creator. For example, `ReducerCreators<State, { asyncThunk: typeof asyncThunkCreator.type }>` would allow you to call `this.asyncThunk`.
-
-:::
-
-:::note Ensuring compatible state
-
-Sometimes it's useful to have a reducer creator that only works with a specific state shape. You can ensure the creator is only callable if the state matches, using a conditional type:
-
-```ts no-transpile
-const loaderCreatorType = Symbol()
-
-declare module '@reduxjs/toolkit' {
-  export interface SliceReducerCreators<
-    State,
-    CaseReducers extends CreatorCaseReducers<State>,
-    Name extends string,
-  > {
-    [loaderCreatorType]: ReducerCreatorEntry<
-      // highlight-next-line
-      State extends { loading: boolean }
-        ? () => {
-            start: CaseReducerDefinition<State, PayloadAction>
-            end: CaseReducerDefinition<State, PayloadAction>
-          }
-        : never
-    >
-  }
-}
-```
-
-Any creators that evaluate to the `never` type are omitted from the final `create` object.
-
-An alternative would be just using that required type _as_ the `State` type for the reducer definitions, so Typescript then complains when the creator is used.
-
-```ts no-transpile
-const loaderCreatorType = Symbol()
-
-declare module '@reduxjs/toolkit' {
-  export interface SliceReducerCreators<
-    State,
-    CaseReducers extends CreatorCaseReducers<State>,
-    Name extends string,
-  > {
-    [loaderCreatorType]: ReducerCreatorEntry<
-      () => {
-        start: CaseReducerDefinition<{ loading: boolean }, PayloadAction>
-        end: CaseReducerDefinition<{ loading: boolean }, PayloadAction>
-      }
-    >
-  }
-}
-```
-
-:::
-
-##### `Exposes` (optional)
-
-The second type parameter for `ReducerCreatorEntry` is optional, but should be used if the creator will handle some reducer definitions itself. It indicates what actions and case reducers will be attached to the slice, and is used to determine the final types of `slice.actions` and `slice.caseReducers`.
-
-It should be an object with some of the following properties:
-
-###### `actions`
-
-The actions property will typically be a [mapped type](https://www.typescriptlang.org/docs/handbook/2/mapped-types.html) over the `CaseReducers` type parameter, returning what the creator's `handle` would expose when given that definition.
-
-In order to ensure that the definitions are correctly filtered to only include those handled by the creator, a conditional type should be used, typically checking the definition extends a `ReducerDefinition` with the right type.
-
-```ts no-transpile
-{
-  [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends ReducerDefinition<typeof creatorType> ? ActionTypeHere : never
-}
-```
-
-For example, with (a simplified version of) the `asyncThunk` creator:
-
-```ts no-transpile
-const asyncThunkCreatorType = Symbol()
-
-declare module '@reduxjs/toolkit' {
-  export interface SliceReducerCreators<
-    State,
-    CaseReducers extends CreatorCaseReducers<State>,
-    Name extends string,
-  > {
-    [asyncThunkCreatorType]: ReducerCreatorEntry<
-      <ThunkArg, Returned>(
-        payloadCreator: AsyncThunkPayloadCreator<ThunkArg, Returned>,
-      ) => AsyncThunkReducerDefinition<State, ThunkArg, Returned>,
-      {
-        // highlight-start
-        actions: {
-          [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends AsyncThunkReducerDefinition<
-            State,
-            infer ThunkArg,
-            infer Returned
-          >
-            ? AsyncThunk<ThunkArg, Returned>
-            : never
-        }
-        // highlight-end
-      }
-    >
-  }
-}
-```
-
-###### `caseReducers`
-
-Similar to `actions`, except for `slice.caseReducers`.
-
-For example, with the `preparedReducer` creator:
-
-```ts no-transpile
-const preparedReducerType = Symbol()
-
-declare module '@reduxjs/toolkit' {
-  export interface SliceReducerCreators<
-    State,
-    CaseReducers extends CreatorCaseReducers<State>,
-    Name extends string,
-  > {
-    [preparedReducerType]: ReducerCreatorEntry<
-      <Prepare extends PrepareAction<any>>(
-        prepare: Prepare,
-        caseReducer: CaseReducer<State, ActionForPrepare<Prepare>>,
-      ) => PreparedCaseReducerDefinition<State, Prepare>,
-      {
-        actions: {
-          [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends ReducerDefinition<
-            typeof preparedReducerType
-          >
-            ? CaseReducers[ReducerName] extends { prepare: any }
-              ? ActionCreatorForCaseReducerWithPrepare<
-                  CaseReducers[ReducerName],
-                  SliceActionType<Name, ReducerName>
-                >
-              : never
-            : never
-        }
-        // highlight-start
-        caseReducers: {
-          [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends ReducerDefinition<
-            typeof preparedReducerType
-          >
-            ? CaseReducers[ReducerName] extends { reducer: infer Reducer }
-              ? Reducer
-              : never
-            : never
-        }
-        // highlight-end
-      }
-    >
-  }
-}
-```
-
-#### Further examples
-
-This section will cover in depth examples, for potential applications with hopefully applicable lessons to other use cases.
-
-If you come up with a novel use for reducer creators, we'd love to hear it! It should even be possible to publish packages with creators for others to use.
-
-:::note `buildCreateSlice` usage
-
-For the sake of a complete example, most of the snippets below will include a `buildCreateSlice` call.
-
-In practicality, we expect that most apps will only call `buildCreateSlice` _once_, with as many creators as needed in that app.
-
-```ts no-transpile
-export const createAppSlice = buildCreateSlice({
-  creators: {
-    toaster: toastCreator,
-    paginationMethods: paginationCreator,
-    historyMethods: historyCreator,
-    undoable: undoableCreator,
-  },
-})
-```
-
-:::
-
-##### Single definitions
-
-Commonly, a creator will return a single reducer definition, to be handled by either itself or another creator.
-
-One example would be reusable toast logic; you could have a reducer creator that makes a thunk creator. That thunk would dispatch an "show" action immediately when called, and then dispatch a second "hide" action after a given amount of time.
-
-```ts no-transpile
-// create the unique type
-const toastCreatorType = Symbol()
-
-interface Toast {
-  message: string
-}
-
-interface ToastReducerConfig<State> {
-  shown?: CaseReducer<State, PayloadAction<Toast, string, { id: string }>>
-  hidden?: CaseReducer<State, PayloadAction<undefined, string, { id: string }>>
-}
-
-interface ToastReducerDefinition<State>
-  extends ReducerDefinition<typeof toastCreatorType>,
-    ToastReducerConfig<State> {}
-
-interface ToastThunkCreator<
-  SliceName extends string,
-  ReducerName extends string,
-> {
-  (
-    toast: Toast,
-    timeout?: number,
-  ): ThunkAction<{ hide(): void }, unknown, unknown, UnknownAction>
-  shown: PayloadActionCreator<
-    Toast,
-    `${SliceActionType<SliceName, ReducerName>}/shown`,
-    (toast: Toast, id: string) => { payload: Toast; meta: { id: string } }
-  >
-  hidden: PayloadActionCreator<
-    void,
-    `${SliceActionType<SliceName, ReducerName>}/hidden`,
-    (id: string) => { payload: undefined; meta: { id: string } }
-  >
-}
-
-// register the creator types
-declare module '@reduxjs/toolkit' {
-  export interface SliceReducerCreators<
-    State,
-    CaseReducers extends CreatorCaseReducers<State>,
-    Name extends string,
-  > {
-    [toastCreatorType]: ReducerCreatorEntry<
-      (config: ToastReducerConfig<State>) => ToastReducerDefinition<State>,
-      {
-        actions: {
-          [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends ToastReducerDefinition<State>
-            ? ToastThunkCreator<Name, ReducerName>
-            : never
-        }
-        caseReducers: {
-          [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends ToastReducerDefinition<State>
-            ? Required<ToastReducerConfig<State>>
-            : never
-        }
-      }
-    >
-  }
-}
-
-// define the creator
-const toastCreator: ReducerCreator<typeof toastCreatorType> = {
-  type: toastCreatorType,
-  // return the reducer definition
-  create(config) {
-    return {
-      _reducerDefinitionType: toastCreatorType,
-      ...config,
-    }
-  },
-  // handle the reducer definition
-  handle({ reducerName, type }, definition, context) {
-    // make the action creators
-    const shown = createAction(type + '/shown', (toast: Toast, id: string) => ({
-      payload: toast,
-      meta: { id },
-    }))
-    const hidden = createAction(type + '/hidden', (id: string) => ({
-      payload: undefined,
-      meta: { id },
-    }))
-    // make the thunk creator
-    function thunkCreator(
-      toast: Toast,
-      timeout = 300,
-    ): ThunkAction<{ hide(): void }, unknown, unknown, UnknownAction> {
-      return (dispatch, getState) => {
-        const id = nanoid()
-        dispatch(shown(toast, id))
-        const timeoutId = setTimeout(() => dispatch(hidden(id)), timeout)
-        return {
-          hide() {
-            clearTimeout(timeoutId)
-            dispatch(hidden(id))
-          },
-        }
-      }
-    }
-    // attach the action creators to the thunk creator
-    Object.assign(thunkCreator, { shown, hidden })
-
-    // add any case reducers passed in the config
-    if (definition.shown) {
-      context.addCase(shown, definition.shown)
-    }
-    if (definition.hidden) {
-      context.addCase(hidden, definition.hidden)
-    }
-
-    // expose the thunk creator as `slice.actions[reducerName]` and the case reducers as `slice.caseReducers[reducerName]["shown" | "hidden"]`
-    context
-      .exposeAction(reducerName, thunkCreator)
-      .exposeCaseReducer(reducerName, {
-        shown: definition.shown || noop,
-        hidden: definition.hidden || noop,
-      })
-  },
-}
-
-function noop() {}
-
-// build the `createSlice` function
-const createAppSlice = buildCreateSlice({
-  creators: { toaster: toastCreator },
-})
-
-const toastSlice = createAppSlice({
-  name: 'toast',
-  initialState: {} as Record<string, Toast>,
-  reducers: (create) => ({
-    // call creator to get definition, and save it to a key
-    showToast: create.toaster({
-      shown(state, action) {
-        state[action.meta.id] = action.payload
-      },
-      hidden(state, action) {
-        delete state[action.meta.id]
-      },
-    }),
-  }),
-})
-
-// showToast is the thunk creator from above
-const { showToast } = toastSlice.actions
-
-// case reducers and action creators are available where we put them
-toastSlice.caseReducers.showToast.hidden({}, showToast.hidden('id'))
-```
-
-##### Multiple definitions
-
-A creator could also return multiple definitions, which would then be spread into the final definitions object. This is a more composable alternative to the [wrapping `createSlice`](usage/usage-with-typescript#wrapping-createslice) approach, as you could call multiple creators as needed.
-
-One example could be returning some pagination related reducers.
-
-```ts no-transpile
-const paginationCreatorType = Symbol()
-
-interface PaginationState {
-  page: number
-}
-
-declare module '@reduxjs/toolkit' {
-  export interface SliceReducerCreators<
-    State,
-    CaseReducers extends CreatorCaseReducers<State>,
-    Name extends string,
-  > {
-    [paginationCreatorType]: ReducerCreatorEntry<
-      // make sure the creator is only called when state is compatible
-      State extends PaginationState
-        ? (this: ReducerCreators<State>) => {
-            prevPage: CaseReducerDefinition<State, PayloadAction>
-            nextPage: CaseReducerDefinition<State, PayloadAction>
-            goToPage: CaseReducerDefinition<State, PayloadAction<number>>
-          }
-        : never
-    >
-  }
-}
-
-const paginationCreator: ReducerCreator<typeof paginationCreatorType> = {
-  type: paginationCreatorType,
-  create() {
-    return {
-      prevPage: this.reducer((state: PaginationState) => {
-        state.page--
-      }),
-      nextPage: this.reducer((state: PaginationState) => {
-        state.page++
-      }),
-      goToPage: this.reducer<number>((state: PaginationState, action) => {
-        state.page = action.payload
-      }),
-    }
-  },
-}
-
-const createAppSlice = buildCreateSlice({
-  creators: { paginationMethods: paginationCreator },
-})
-
-const paginationSlice = createAppSlice({
-  name: 'pagination',
-  initialState: { page: 0, loading: false },
-  reducers: (create) => ({
-    ...create.paginationMethods(),
-    toggleLoading: create.reducer((state) => {
-      state.loading = !state.loading
-    }),
-  }),
-})
-
-const { prevPage, nextPage, goToPage, toggleLoading } = paginationSlice.actions
-```
-
-A creator could return a mix of reducer definitions for itself and other creators to handle:
-
-```ts no-transpile
-const historyCreatorType = Symbol()
-
-interface PatchesState {
-  undo: Patch[]
-  redo: Patch[]
-}
-
-interface HistoryState<T> {
-  past: PatchesState[]
-  present: T
-  future: PatchesState[]
-}
-
-declare module '@reduxjs/toolkit' {
-  export interface SliceReducerCreators<
-    State,
-    CaseReducers extends CreatorCaseReducers<State>,
-    Name extends string,
-  > {
-    [historyCreatorType]: ReducerCreatorEntry<
-      // make sure the creator is only called when state is compatibleState extends HistoryState<unknown>
-      State extends HistoryState<any>
-        ? (this: ReducerCreators<State>) => {
-            undo: CaseReducerDefinition<State, PayloadAction>
-            redo: CaseReducerDefinition<State, PayloadAction>
-            reset: ReducerDefinition<typeof historyCreatorType> & {
-              type: 'reset'
-            }
-          }
-        : never,
-      {
-        actions: {
-          [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends ReducerDefinition<
-            typeof historyCreatorType
-          >
-            ? CaseReducers[ReducerName] extends { type: 'reset' }
-              ? PayloadActionCreator<void, SliceActionType<Name, ReducerName>>
-              : never
-            : never
-        }
-        caseReducers: {
-          [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends ReducerDefinition<
-            typeof historyCreatorType
-          >
-            ? CaseReducers[ReducerName] extends { type: 'reset' }
-              ? CaseReducer<State, PayloadAction>
-              : never
-            : never
-        }
-      }
-    >
-  }
-}
-
-const historyCreator: ReducerCreator<typeof historyCreatorType> = {
-  type: historyCreatorType,
-  create() {
-    return {
-      undo: this.reducer((state: HistoryState<unknown>) => {
-        const historyEntry = state.past.pop()
-        if (historyEntry) {
-          applyPatches(state, historyEntry.undo)
-          state.future.unshift(historyEntry)
-        }
-      }),
-      redo: this.reducer((state: HistoryState<unknown>) => {
-        const historyEntry = state.future.shift()
-        if (historyEntry) {
-          applyPatches(state, historyEntry.redo)
-          state.past.push(historyEntry)
-        }
-      }),
-      reset: {
-        _reducerDefinitionType: historyCreatorType,
-        type: 'reset',
-      },
-    }
-  },
-  handle(details, definition, context) {
-    if (definition.type !== 'reset') {
-      throw new Error('Unrecognised definition type: ' + definition.type)
-    }
-    // use the normal reducer creator to create a case reducer and action creator
-    const resetReducer = () => context.getInitialState()
-    reducerCreator.handle(details, reducerCreator.create(resetReducer), context)
-  },
-}
-
-const createAppSlice = buildCreateSlice({
-  creators: { historyMethods: historyCreator },
-})
-
-function getInitialHistoryState<T>(initialState: T): HistoryState<T> {
-  return {
-    past: [],
-    present: initialState,
-    future: [],
-  }
-}
-
-const postSliceWithHistory = createAppSlice({
-  name: 'post',
-  initialState: getInitialHistoryState({ title: '' }),
-  reducers: (create) => ({
-    ...create.historyMethods(),
-  }),
-})
-
-const { undo, redo, reset } = postSliceWithHistory.actions
-```
-
-##### Other
-
-A creator doesn't have to return any reducer definitions, it could be any sort of utility for defining reducers.
-
-Following on from the `HistoryState` example above, it would be useful to make some sort of `undoable` utility to wrap reducers in logic which automatically updates the history of the slice.
-
-Fortunately, this is possible with a creator:
-
-```ts no-transpile
-const undoableCreatorType = Symbol()
-
-interface UndoableMeta {
-  undoable?: boolean
-}
-
-declare module '@reduxjs/toolkit' {
-  export interface SliceReducerCreators<
-    State,
-    CaseReducers extends CreatorCaseReducers<State>,
-    Name extends string,
-  > {
-    [undoableCreatorType]: ReducerCreatorEntry<
-      State extends HistoryState<infer Data>
-        ? {
-            <A extends Action & { meta?: UndoableMeta }>(
-              reducer: CaseReducer<Data, A>,
-            ): CaseReducer<State, A>
-            withoutPayload(options?: UndoableMeta): {
-              payload: undefined
-              meta: UndoableMeta | undefined
-            }
-            withPayload<Payload>(
-              payload: Payload,
-              options?: UndoableMeta,
-            ): { payload: Payload; meta: UndoableMeta | undefined }
-          }
-        : never
-    >
-  }
-}
-
-const undoableCreator: ReducerCreator<typeof undoableCreatorType> = {
-  type: undoableCreatorType,
-  create: Object.assign(
-    function makeUndoable<A extends Action & { meta?: UndoableOptions }>(
-      reducer: CaseReducer<any, A>,
-    ): CaseReducer<HistoryState<any>, A> {
-      return (state, action) => {
-        const [nextState, redoPatch, undoPatch] = produceWithPatches(
-          state,
-          (draft) => {
-            const result = reducer(draft.present, action)
-            if (typeof result !== 'undefined') {
-              draft.present = result
-            }
-          },
-        )
-        let finalState = nextState
-        const undoable = action.meta?.undoable ?? true
-        if (undoable) {
-          finalState = createNextState(finalState, (draft) => {
-            draft.past.push({
-              undo: undoPatch,
-              redo: redoPatch,
-            })
-            draft.future = []
-          })
-        }
-        return finalState
-      }
-    },
-    {
-      withoutPayload() {
-        return (options?: UndoableOptions) => ({
-          payload: undefined,
-          meta: options,
-        })
-      },
-      withPayload<P>() {
-        return (
-          ...[payload, options]: IfMaybeUndefined<
-            P,
-            [payload?: P, options?: UndoableOptions],
-            [payload: P, options?: UndoableOptions]
-          >
-        ) => ({ payload: payload as P, meta: options })
-      },
-    },
-  ),
-}
-
-const createAppSlice = buildCreateSlice({
-  creators: { historyMethods: historyCreator, undoable: undoableCreator },
-})
-
-const postSliceWithHistory = createAppSlice({
-  name: 'post',
-  initialState: getInitialHistoryState({ title: '', pinned: false }),
-  reducers: (create) => ({
-    ...create.historyMethods(),
-    updateTitle: create.preparedReducer(
-      create.undoable.withPayload<string>(),
-      create.undoable((state, action) => {
-        state.title = action.payload
-      }),
-    ),
-    togglePinned: create.preparedReducer(
-      create.undoable.withoutPayload(),
-      create.undoable((state, action) => {
-        state.pinned = !state.pinned
-      }),
-    ),
-  }),
-})
-
-const { undo, redo, reset, updateTitle, togglePinned } =
-  postSliceWithHistory.actions
-```
diff --git a/docs/usage/custom-slice-creators.md b/docs/usage/custom-slice-creators.md
deleted file mode 100644
index 99331064da..0000000000
--- a/docs/usage/custom-slice-creators.md
+++ /dev/null
@@ -1,8 +0,0 @@
----
-id: custom-slice-creators
-title: Custom Slice Creators
-sidebar_label: Custom Slice Creators
-hide_title: true
----
-
-# Custom Slice Creators
diff --git a/docs/usage/custom-slice-creators.mdx b/docs/usage/custom-slice-creators.mdx
new file mode 100644
index 0000000000..4d534afbb5
--- /dev/null
+++ b/docs/usage/custom-slice-creators.mdx
@@ -0,0 +1,1201 @@
+---
+id: custom-slice-creators
+title: Custom Slice Creators
+sidebar_label: Custom Slice Creators
+hide_title: true
+---
+
+# Custom Slice Creators
+
+## Slice creators
+
+Redux Toolkit 2.0 introduces the concept of "slice creators", which allow you to define reusable logic for creating slices.
+
+These "creators" have the capability to:
+
+- Define multiple reducers at the same time
+- Modify slice behaviour by adding case/matcher reducers
+- Expose custom actions (thunks, for example) and case reducers
+
+```ts no-transpile
+const createAppSlice = buildCreateSlice({
+  creators: { historyMethods: historyCreator, undoable: undoableCreator },
+})
+
+const postSliceWithHistory = createAppSlice({
+  name: 'post',
+  initialState: getInitialHistoryState({ title: '', pinned: false }),
+  reducers: (create) => ({
+    ...create.historyMethods(),
+    updateTitle: create.preparedReducer(
+      create.undoable.withPayload<string>(),
+      create.undoable((state, action) => {
+        state.title = action.payload
+      }),
+    ),
+    togglePinned: create.preparedReducer(
+      create.undoable.withoutPayload(),
+      create.undoable((state, action) => {
+        state.pinned = !state.pinned
+      }),
+    ),
+  }),
+})
+
+const { undo, redo, reset, updateTitle, togglePinned } =
+  postSliceWithHistory.actions
+```
+
+### The `reducers` "creator callback" notation
+
+In order to use slice creators, `reducers` becomes a callback, which receives a `create` object. This `create` object contains a couple of [inbuilt creators](#rtk-creators), along with any creators passed to [`buildCreateSlice`](#buildcreateslice).
+
+```ts title="Creator callback for reducers"
+import { buildCreateSlice, asyncThunkCreator, nanoid } from '@reduxjs/toolkit'
+
+const createAppSlice = buildCreateSlice({
+  creators: { asyncThunk: asyncThunkCreator },
+})
+
+interface Item {
+  id: string
+  text: string
+}
+
+interface TodoState {
+  loading: boolean
+  todos: Item[]
+}
+
+const todosSlice = createAppSlice({
+  name: 'todos',
+  initialState: {
+    loading: false,
+    todos: [],
+  } as TodoState,
+  reducers: (create) => ({
+    deleteTodo: create.reducer<number>((state, action) => {
+      state.todos.splice(action.payload, 1)
+    }),
+    addTodo: create.preparedReducer(
+      (text: string) => {
+        const id = nanoid()
+        return { payload: { id, text } }
+      },
+      // action type is inferred from prepare callback
+      (state, action) => {
+        state.todos.push(action.payload)
+      },
+    ),
+    fetchTodo: create.asyncThunk(
+      async (id: string, thunkApi) => {
+        const res = await fetch(`myApi/todos?id=${id}`)
+        return (await res.json()) as Item
+      },
+      {
+        pending: (state) => {
+          state.loading = true
+        },
+        rejected: (state, action) => {
+          state.loading = false
+        },
+        fulfilled: (state, action) => {
+          state.loading = false
+          state.todos.push(action.payload)
+        },
+      },
+    ),
+  }),
+})
+
+export const { addTodo, deleteTodo, fetchTodo } = todosSlice.actions
+```
+
+#### RTK Creators
+
+These creators come built into RTK, and are always available on the `create` object passed to the `reducers` callback.
+
+##### `create.reducer`
+
+A standard slice case reducer. Creates an action creator with the same name as the reducer.
+
+**Parameters**
+
+- **reducer** The slice case reducer to use.
+
+```ts no-transpile
+create.reducer<Todo>((state, action) => {
+  state.todos.push(action.payload)
+})
+```
+
+:::tip
+
+The [creator definition](#creator-definitions) for `create.reducer` is exported from RTK as `reducerCreator`, to allow reuse.
+
+:::
+
+##### `create.preparedReducer`
+
+A [prepared](#customizing-generated-action-creators) reducer, to customize the action creator. Creates a prepared action creator with the same name as the reducer.
+
+**Parameters**
+
+- **prepareAction** The [`prepare callback`](./createAction#using-prepare-callbacks-to-customize-action-contents).
+- **reducer** The slice case reducer to use.
+
+The action passed to the case reducer will be inferred from the prepare callback's return.
+
+```ts no-transpile
+create.preparedReducer(
+  (text: string) => {
+    const id = nanoid()
+    return { payload: { id, text } }
+  },
+  (state, action) => {
+    state.todos.push(action.payload)
+  },
+)
+```
+
+:::tip
+
+The [creator definition](#creator-definitions) for `create.preparedReducer` is exported from RTK as `preparedReducerCreator`, to allow reuse.
+
+:::
+
+#### Optional RTK Creators
+
+These creators are not included in the default `create` object, but can be added by passing them to [`buildCreateSlice`](#buildcreateslice).
+
+The name the creator is available under is based on the key used when calling `buildCreateSlice`. For example, to use `create.asyncThunk`:
+
+```ts
+import { buildCreateSlice, asyncThunkCreator } from '@reduxjs/toolkit'
+
+export const createAppSlice = buildCreateSlice({
+  creators: {
+    // highlight-next-line
+    asyncThunk: asyncThunkCreator,
+  },
+})
+
+interface Post {
+  id: string
+  text: string
+}
+
+export const postsSlice = createAppSlice({
+  name: 'posts',
+  initialState: [] as Post[],
+  reducers: (create) => ({
+    // highlight-next-line
+    fetchPosts: create.asyncThunk(
+      async () => {
+        const res = await fetch('myApi/posts')
+        return (await res.json()) as Post[]
+      },
+      {
+        fulfilled(state, action) {
+          return action.payload
+        },
+      },
+    ),
+  }),
+})
+```
+
+For clarity these docs will use recommended names.
+
+:::tip
+
+We recommend using `createAppSlice` consistently throughout your app as a replacement for `createSlice`.
+
+This avoids having to consider whether the creators are needed for each slice.
+
+:::
+
+:::caution
+
+To avoid collision, names used by [RTK creators](#rtk-creators) are reserved - passing creators under the `reducer` or `preparedReducer` keys is not allowed, and only `asyncThunkCreator` is allowed to be passed under the `asyncThunk` key.
+
+```ts no-transpile
+const createAppSlice = buildCreateSlice({
+  creators: {
+    reducer: aCustomCreator, // not allowed, name is reserved
+    asyncThunk: aCustomCreator, // not allowed, must be asyncThunkCreator
+    asyncThunk: asyncThunkCreator, // allowed
+  },
+})
+```
+
+:::
+
+##### `create.asyncThunk` (`asyncThunkCreator`)
+
+Creates an async thunk and adds any provided case reducers for lifecycle actions.
+
+**Parameters**
+
+- **payloadCreator** The thunk [payload creator](./createAsyncThunk#payloadcreator).
+- **config** The configuration object. (optional)
+
+The configuration object can contain case reducers for each of the [lifecycle actions](./createAsyncThunk#promise-lifecycle-actions) (`pending`, `fulfilled`, and `rejected`), as well as a `settled` reducer that will run for both fulfilled and rejected actions (note that this will run _after_ any provided `fulfilled`/`rejected` reducers. Conceptually it can be thought of like a `finally` block.).
+
+Each case reducer will be attached to the slice's `caseReducers` object, e.g. `slice.caseReducers.fetchTodo.fulfilled`.
+
+The configuration object can also contain [`options`](./createAsyncThunk#options).
+
+```ts no-transpile
+create.asyncThunk(
+  async (id: string, thunkApi) => {
+    const res = await fetch(`myApi/todos?id=${id}`)
+    return (await res.json()) as Item
+  },
+  {
+    pending: (state) => {
+      state.loading = true
+    },
+    rejected: (state, action) => {
+      state.error = action.payload ?? action.error
+    },
+    fulfilled: (state, action) => {
+      state.todos.push(action.payload)
+    },
+    settled: (state, action) => {
+      state.loading = false
+    }
+    options: {
+      idGenerator: uuid,
+    },
+  }
+)
+```
+
+:::note
+
+Typing for `create.asyncThunk` works in the same way as [`createAsyncThunk`](../usage/usage-with-typescript#createasyncthunk), with one key difference.
+
+A type for `state` and/or `dispatch` _cannot_ be provided as part of the `ThunkApiConfig`, as this would cause circular types.
+
+Instead, it is necessary to assert the type when needed - `getState() as RootState`. You may also include an explicit return type for the payload function as well, in order to break the circular type inference cycle.
+
+```ts no-transpile
+create.asyncThunk<Todo, string, { rejectValue: { error: string } }>(
+  // highlight-start
+  // may need to include an explicit return type
+  async (id: string, thunkApi): Promise<Todo> => {
+    // Cast types for `getState` and `dispatch` manually
+    const state = thunkApi.getState() as RootState
+    const dispatch = thunkApi.dispatch as AppDispatch
+    // highlight-end
+    try {
+      const todo = await fetchTodo()
+      return todo
+    } catch (e) {
+      throw thunkApi.rejectWithValue({
+        error: 'Oh no!',
+      })
+    }
+  },
+)
+```
+
+For common thunk API configuration options, a [`withTypes` helper](../usage/usage-with-typescript#defining-a-pre-typed-createasyncthunk) is provided:
+
+```ts no-transpile
+reducers: (create) => {
+  const createAThunk = create.asyncThunk.withTypes<{
+    rejectValue: { error: string }
+  }>()
+
+  return {
+    fetchTodo: createAThunk<Todo, string>(async (id, thunkApi) => {
+      throw thunkApi.rejectWithValue({
+        error: 'Oh no!',
+      })
+    }),
+    fetchTodos: createAThunk<Todo[], string>(async (id, thunkApi) => {
+      throw thunkApi.rejectWithValue({
+        error: 'Oh no, not again!',
+      })
+    }),
+  }
+}
+```
+
+:::
+
+### Writing your own creators
+
+In version v2.3.0, we introduced a system for including your own creators.
+
+The below documentation will cover how to write your own creators, and how to use them with `createSlice`.
+
+#### Reducer definitions
+
+A reducer definition is an object (or function) with a `_reducerDefinitionType` property indicating which creator should handle it. Other than this property, it is entirely up to the creator what this definition object can look like.
+
+For example, the `create.preparedReducer` creator uses a definition that looks like `{ prepare, reducer }`.
+
+The callback form of `reducers` should return an object of reducer definitions, by calling creators and nesting the result of each under a key.
+
+```js no-transpile
+reducers: (create) => ({
+  addTodo: create.preparedReducer(
+    (todo) => ({ payload: { id: nanoid(), ...todo } }),
+    (state, action) => {
+      state.push(action.payload)
+    },
+  ),
+})
+// becomes
+const definitions = {
+  addTodo: {
+    _reducerDefinitionType: 'reducerWithPrepare',
+    prepare: (todo) => ({ payload: { id: nanoid(), ...todo } }),
+    reducer: (state, action) => {
+      state.push(action.payload)
+    },
+  },
+}
+```
+
+Typically a creator will return a [single reducer definition](#single-definitions), but it could return an object of [multiple definitions](#multiple-definitions) to be spread into the final object, or [something else entirely](#other)!
+
+#### Creator definitions
+
+A creator definition contains the actual runtime logic for that creator. It's an object with a `type` property, a `create` method, and an optional `handle` method.
+
+It's passed to [`buildCreateSlice`](#buildcreateslice) as part of the `creators` object, and the name used when calling `buildCreateSlice` will be the key the creator is nested under in the `create` object.
+
+```ts no-transpile
+import { buildCreateSlice } from '@reduxjs/toolkit'
+
+const createAppSlice = buildCreateSlice({
+  // highlight-next-line
+  creators: { batchable: batchableCreator },
+})
+
+const todoSlice = createSlice({
+  name: 'todos',
+  initialState: [] as Todo[],
+  reducers: (create) => ({
+    // highlight-next-line
+    addTodo: create.batchable<Todo>((state, action) => {
+      state.push(action.payload)
+    }),
+  }),
+})
+```
+
+The `type` property of the definition should be the same constant used for reducer definitions to be handled by this creator. To avoid collision, we recommend using Symbols for this. It's also used for defining/retrieving types - see [Typescript](#typescript).
+
+```ts no-transpile
+const reducerCreatorType = Symbol()
+
+const reducerCreator: ReducerCreator<typeof reducerCreatorType> = {
+  type: reducerCreatorType,
+  create(reducer) {
+    return {
+      _reducerDefinitionType: reducerCreatorType,
+      reducer,
+    }
+  },
+  handle({ reducerName, type }, definition, context) {
+    const { reducer } = definition
+    const actionCreator = createAction(type)
+    context
+      .addCase(actionCreator, reducer)
+      .exposeAction(reducerName, actionCreator)
+      .exposeCaseReducer(reducerName, reducer)
+  },
+}
+```
+
+##### `create`
+
+The `create` method is the function that will be attached to the `create` object, before it's passed to the `reducers` callback.
+
+Because it's a function, the `this` value will be the final `create` object when called (assuming a `create.creator()` call). It also could have additional methods attached.
+
+See the [Further examples](#further-examples) section for some examples of these.
+
+#### `handle`
+
+The `handle` callback of a creator will be called for any reducer definitions with a matching `_reducerDefinitionType` property.
+
+:::note
+A creator only needs a `handle` callback if it expects to be called with reducer definitions. If it only calls other creators (see [Using `this` to access other creators](#create-1)), it can omit the `handle`.
+:::
+
+It receives three arguments: details about the reducer, the definition, and a `context` object with methods to modify the slice.
+
+The reducer details object has two properties:
+
+- `reducerName` - the key the reducer definition was under (e.g. `addTodo`)
+- `type` - the automatically generated type string for the reducer (e.g. `todos/addTodo`)
+
+The context object includes:
+
+##### `addCase`
+
+The same as [`addCase`](./createReducer#builderaddcase) for `createReducer` and `extraReducers`. Adds a case reducer for a given action type, and can receive an action type string or an action creator with a `.type` property.
+
+```ts no-transpile
+const action = createAction(type)
+context.addCase(action, reducer)
+```
+
+##### `addMatcher`
+
+The same as [`addMatcher`](./createReducer#builderaddmatcher) for `createReducer` and `extraReducers`. Adds a case reducer which will be called when a given matcher returns true.
+
+```ts no-transpile
+const matcher = isAnyOf(action, action2)
+context.addMatcher(matcher, reducer)
+```
+
+##### `exposeAction`
+
+Attaches a value to `slice.actions`. Receives the key to be set under (typically `reducerName`) and the value to be set.
+
+```ts no-transpile
+const action = createAction(type)
+context.exposeAction(reducerName, action)
+```
+
+##### `exposeCaseReducer`
+
+Attaches a value to `slice.caseReducers`. Receives the key to be set under (typically `reducerName`) and the value to be set.
+
+```ts no-transpile
+context.exposeCaseReducer(reducerName, reducer)
+```
+
+##### `getInitialState`
+
+Returns the initial state value for the slice. If a lazy state initializer has been provided, it will be called and a fresh value returned.
+
+```ts no-transpile
+const resetAction = createAction(type)
+const resetReducer = () => context.getInitialState()
+context
+  .addCase(resetAction, resetReducer)
+  .exposeAction(reducerName, resetAction)
+  .exposeCaseReducer(reducerName, resetReducer)
+```
+
+#### Typescript
+
+The Typescript system for custom slice creators uses a "creator registry" system similar to the module system for [RTK Query](/rtk-query/usage/customizing-create-api#creating-your-own-module).
+
+Creators are registered by using module augmentation to add a new key (their unique `type`) to the `SliceReducerCreators` interface. The interface receives three type parameters (`State`, `CaseReducers` and `Name`), and each entry should use the `ReducerCreatorEntry` type utility.
+
+```ts no-transpile
+const reducerCreatorType = Symbol()
+
+declare module '@reduxjs/toolkit' {
+  export interface SliceReducerCreators<
+    State,
+    CaseReducers extends CreatorCaseReducers<State>,
+    Name extends string,
+  > {
+    [reducerCreatorType]: ReducerCreatorEntry<
+      () => ReducerDefinition<typeof reducerCreatorType>
+    >
+  }
+}
+```
+
+The type parameters for `SliceReducerCreators` are:
+
+- `State` - The state type used by the slice.
+- `CaseReducers` - The case reducer definitions returned by the creator callback.
+- `Name` - The [`name`](#name) used by the slice.
+
+The `ReducerCreatorEntry<Create, Exposes>` utility has two type parameters:
+
+##### `Create`
+
+The signature of the `create` method of the creator definition.
+
+:::caution `CaseReducers` and `Name`
+
+Your `Create` type should not depend on the `CaseReducers` and `Name` type parameters, as these will not yet exist when the creator is being called.
+
+:::
+
+:::tip Using `this` to access other creators
+
+Assuming the creator is called as `create.yourCreator()`, the `this` value for the function is the `create` object - meaning you can call other creators on the same object.
+
+However, this should be specifically included in the function signature, so Typescript can warn if called with an incorrect context (for example, if the user destructures from the `create` value).
+
+```ts no-transpile
+const batchedCreatorType = Symbol()
+
+declare module '@reduxjs/toolkit' {
+  export interface SliceReducerCreators<
+    State,
+    CaseReducers extends CreatorCaseReducers<State>,
+    Name extends string,
+  > {
+    [batchedCreatorType]: ReducerCreatorEntry<
+      <Payload>(
+        // highlight-next-line
+        this: ReducerCreators<State, {}>,
+        reducer: CaseReducer<State, PayloadAction<Payload>>,
+      ) => PreparedCaseReducerDefinition<
+        State,
+        (payload: Payload) => { payload: Payload; meta: unknown }
+      >
+    >
+  }
+}
+
+const batchedCreator: ReducerCreator<typeof batchedCreatorType> = {
+  type: batchedCreatorType,
+  create(reducer) {
+    return this.preparedReducer(prepareAutoBatched(), reducer)
+  },
+}
+```
+
+The second argument to the `ReducerCreators` type is a map from creator names to types, which you should supply if you're expecting to use any custom creators (anything other than `reducer` and `preparedReducer`) within your own creator. For example, `ReducerCreators<State, { asyncThunk: typeof asyncThunkCreator.type }>` would allow you to call `this.asyncThunk`.
+
+:::
+
+:::note Ensuring compatible state
+
+Sometimes it's useful to have a reducer creator that only works with a specific state shape. You can ensure the creator is only callable if the state matches, using a conditional type:
+
+```ts no-transpile
+const loaderCreatorType = Symbol()
+
+declare module '@reduxjs/toolkit' {
+  export interface SliceReducerCreators<
+    State,
+    CaseReducers extends CreatorCaseReducers<State>,
+    Name extends string,
+  > {
+    [loaderCreatorType]: ReducerCreatorEntry<
+      // highlight-next-line
+      State extends { loading: boolean }
+        ? () => {
+            start: CaseReducerDefinition<State, PayloadAction>
+            end: CaseReducerDefinition<State, PayloadAction>
+          }
+        : never
+    >
+  }
+}
+```
+
+Any creators that evaluate to the `never` type are omitted from the final `create` object.
+
+An alternative would be just using that required type _as_ the `State` type for the reducer definitions, so Typescript then complains when the creator is used.
+
+```ts no-transpile
+const loaderCreatorType = Symbol()
+
+declare module '@reduxjs/toolkit' {
+  export interface SliceReducerCreators<
+    State,
+    CaseReducers extends CreatorCaseReducers<State>,
+    Name extends string,
+  > {
+    [loaderCreatorType]: ReducerCreatorEntry<
+      () => {
+        start: CaseReducerDefinition<{ loading: boolean }, PayloadAction>
+        end: CaseReducerDefinition<{ loading: boolean }, PayloadAction>
+      }
+    >
+  }
+}
+```
+
+:::
+
+##### `Exposes` (optional)
+
+The second type parameter for `ReducerCreatorEntry` is optional, but should be used if the creator will handle some reducer definitions itself. It indicates what actions and case reducers will be attached to the slice, and is used to determine the final types of `slice.actions` and `slice.caseReducers`.
+
+It should be an object with some of the following properties:
+
+###### `actions`
+
+The actions property will typically be a [mapped type](https://www.typescriptlang.org/docs/handbook/2/mapped-types.html) over the `CaseReducers` type parameter, returning what the creator's `handle` would expose when given that definition.
+
+In order to ensure that the definitions are correctly filtered to only include those handled by the creator, a conditional type should be used, typically checking the definition extends a `ReducerDefinition` with the right type.
+
+```ts no-transpile
+{
+  [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends ReducerDefinition<typeof creatorType> ? ActionTypeHere : never
+}
+```
+
+For example, with (a simplified version of) the `asyncThunk` creator:
+
+```ts no-transpile
+const asyncThunkCreatorType = Symbol()
+
+declare module '@reduxjs/toolkit' {
+  export interface SliceReducerCreators<
+    State,
+    CaseReducers extends CreatorCaseReducers<State>,
+    Name extends string,
+  > {
+    [asyncThunkCreatorType]: ReducerCreatorEntry<
+      <ThunkArg, Returned>(
+        payloadCreator: AsyncThunkPayloadCreator<ThunkArg, Returned>,
+      ) => AsyncThunkReducerDefinition<State, ThunkArg, Returned>,
+      {
+        // highlight-start
+        actions: {
+          [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends AsyncThunkReducerDefinition<
+            State,
+            infer ThunkArg,
+            infer Returned
+          >
+            ? AsyncThunk<ThunkArg, Returned>
+            : never
+        }
+        // highlight-end
+      }
+    >
+  }
+}
+```
+
+###### `caseReducers`
+
+Similar to `actions`, except for `slice.caseReducers`.
+
+For example, with the `preparedReducer` creator:
+
+```ts no-transpile
+const preparedReducerType = Symbol()
+
+declare module '@reduxjs/toolkit' {
+  export interface SliceReducerCreators<
+    State,
+    CaseReducers extends CreatorCaseReducers<State>,
+    Name extends string,
+  > {
+    [preparedReducerType]: ReducerCreatorEntry<
+      <Prepare extends PrepareAction<any>>(
+        prepare: Prepare,
+        caseReducer: CaseReducer<State, ActionForPrepare<Prepare>>,
+      ) => PreparedCaseReducerDefinition<State, Prepare>,
+      {
+        actions: {
+          [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends ReducerDefinition<
+            typeof preparedReducerType
+          >
+            ? CaseReducers[ReducerName] extends { prepare: any }
+              ? ActionCreatorForCaseReducerWithPrepare<
+                  CaseReducers[ReducerName],
+                  SliceActionType<Name, ReducerName>
+                >
+              : never
+            : never
+        }
+        // highlight-start
+        caseReducers: {
+          [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends ReducerDefinition<
+            typeof preparedReducerType
+          >
+            ? CaseReducers[ReducerName] extends { reducer: infer Reducer }
+              ? Reducer
+              : never
+            : never
+        }
+        // highlight-end
+      }
+    >
+  }
+}
+```
+
+#### Further examples
+
+This section will cover in depth examples, for potential applications with hopefully applicable lessons to other use cases.
+
+If you come up with a novel use for reducer creators, we'd love to hear it! It should even be possible to publish packages with creators for others to use.
+
+:::note `buildCreateSlice` usage
+
+For the sake of a complete example, most of the snippets below will include a `buildCreateSlice` call.
+
+In practicality, we expect that most apps will only call `buildCreateSlice` _once_, with as many creators as needed in that app.
+
+```ts no-transpile
+export const createAppSlice = buildCreateSlice({
+  creators: {
+    toaster: toastCreator,
+    paginationMethods: paginationCreator,
+    historyMethods: historyCreator,
+    undoable: undoableCreator,
+  },
+})
+```
+
+:::
+
+##### Single definitions
+
+Commonly, a creator will return a single reducer definition, to be handled by either itself or another creator.
+
+One example would be reusable toast logic; you could have a reducer creator that makes a thunk creator. That thunk would dispatch an "show" action immediately when called, and then dispatch a second "hide" action after a given amount of time.
+
+```ts no-transpile
+// create the unique type
+const toastCreatorType = Symbol()
+
+interface Toast {
+  message: string
+}
+
+interface ToastReducerConfig<State> {
+  shown?: CaseReducer<State, PayloadAction<Toast, string, { id: string }>>
+  hidden?: CaseReducer<State, PayloadAction<undefined, string, { id: string }>>
+}
+
+interface ToastReducerDefinition<State>
+  extends ReducerDefinition<typeof toastCreatorType>,
+    ToastReducerConfig<State> {}
+
+interface ToastThunkCreator<
+  SliceName extends string,
+  ReducerName extends string,
+> {
+  (
+    toast: Toast,
+    timeout?: number,
+  ): ThunkAction<{ hide(): void }, unknown, unknown, UnknownAction>
+  shown: PayloadActionCreator<
+    Toast,
+    `${SliceActionType<SliceName, ReducerName>}/shown`,
+    (toast: Toast, id: string) => { payload: Toast; meta: { id: string } }
+  >
+  hidden: PayloadActionCreator<
+    void,
+    `${SliceActionType<SliceName, ReducerName>}/hidden`,
+    (id: string) => { payload: undefined; meta: { id: string } }
+  >
+}
+
+// register the creator types
+declare module '@reduxjs/toolkit' {
+  export interface SliceReducerCreators<
+    State,
+    CaseReducers extends CreatorCaseReducers<State>,
+    Name extends string,
+  > {
+    [toastCreatorType]: ReducerCreatorEntry<
+      (config: ToastReducerConfig<State>) => ToastReducerDefinition<State>,
+      {
+        actions: {
+          [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends ToastReducerDefinition<State>
+            ? ToastThunkCreator<Name, ReducerName>
+            : never
+        }
+        caseReducers: {
+          [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends ToastReducerDefinition<State>
+            ? Required<ToastReducerConfig<State>>
+            : never
+        }
+      }
+    >
+  }
+}
+
+// define the creator
+const toastCreator: ReducerCreator<typeof toastCreatorType> = {
+  type: toastCreatorType,
+  // return the reducer definition
+  create(config) {
+    return {
+      _reducerDefinitionType: toastCreatorType,
+      ...config,
+    }
+  },
+  // handle the reducer definition
+  handle({ reducerName, type }, definition, context) {
+    // make the action creators
+    const shown = createAction(type + '/shown', (toast: Toast, id: string) => ({
+      payload: toast,
+      meta: { id },
+    }))
+    const hidden = createAction(type + '/hidden', (id: string) => ({
+      payload: undefined,
+      meta: { id },
+    }))
+    // make the thunk creator
+    function thunkCreator(
+      toast: Toast,
+      timeout = 300,
+    ): ThunkAction<{ hide(): void }, unknown, unknown, UnknownAction> {
+      return (dispatch, getState) => {
+        const id = nanoid()
+        dispatch(shown(toast, id))
+        const timeoutId = setTimeout(() => dispatch(hidden(id)), timeout)
+        return {
+          hide() {
+            clearTimeout(timeoutId)
+            dispatch(hidden(id))
+          },
+        }
+      }
+    }
+    // attach the action creators to the thunk creator
+    Object.assign(thunkCreator, { shown, hidden })
+
+    // add any case reducers passed in the config
+    if (definition.shown) {
+      context.addCase(shown, definition.shown)
+    }
+    if (definition.hidden) {
+      context.addCase(hidden, definition.hidden)
+    }
+
+    // expose the thunk creator as `slice.actions[reducerName]` and the case reducers as `slice.caseReducers[reducerName]["shown" | "hidden"]`
+    context
+      .exposeAction(reducerName, thunkCreator)
+      .exposeCaseReducer(reducerName, {
+        shown: definition.shown || noop,
+        hidden: definition.hidden || noop,
+      })
+  },
+}
+
+function noop() {}
+
+// build the `createSlice` function
+const createAppSlice = buildCreateSlice({
+  creators: { toaster: toastCreator },
+})
+
+const toastSlice = createAppSlice({
+  name: 'toast',
+  initialState: {} as Record<string, Toast>,
+  reducers: (create) => ({
+    // call creator to get definition, and save it to a key
+    showToast: create.toaster({
+      shown(state, action) {
+        state[action.meta.id] = action.payload
+      },
+      hidden(state, action) {
+        delete state[action.meta.id]
+      },
+    }),
+  }),
+})
+
+// showToast is the thunk creator from above
+const { showToast } = toastSlice.actions
+
+// case reducers and action creators are available where we put them
+toastSlice.caseReducers.showToast.hidden({}, showToast.hidden('id'))
+```
+
+##### Multiple definitions
+
+A creator could also return multiple definitions, which would then be spread into the final definitions object. This is a more composable alternative to the [wrapping `createSlice`](usage/usage-with-typescript#wrapping-createslice) approach, as you could call multiple creators as needed.
+
+One example could be returning some pagination related reducers.
+
+```ts no-transpile
+const paginationCreatorType = Symbol()
+
+interface PaginationState {
+  page: number
+}
+
+declare module '@reduxjs/toolkit' {
+  export interface SliceReducerCreators<
+    State,
+    CaseReducers extends CreatorCaseReducers<State>,
+    Name extends string,
+  > {
+    [paginationCreatorType]: ReducerCreatorEntry<
+      // make sure the creator is only called when state is compatible
+      State extends PaginationState
+        ? (this: ReducerCreators<State>) => {
+            prevPage: CaseReducerDefinition<State, PayloadAction>
+            nextPage: CaseReducerDefinition<State, PayloadAction>
+            goToPage: CaseReducerDefinition<State, PayloadAction<number>>
+          }
+        : never
+    >
+  }
+}
+
+const paginationCreator: ReducerCreator<typeof paginationCreatorType> = {
+  type: paginationCreatorType,
+  create() {
+    return {
+      prevPage: this.reducer((state: PaginationState) => {
+        state.page--
+      }),
+      nextPage: this.reducer((state: PaginationState) => {
+        state.page++
+      }),
+      goToPage: this.reducer<number>((state: PaginationState, action) => {
+        state.page = action.payload
+      }),
+    }
+  },
+}
+
+const createAppSlice = buildCreateSlice({
+  creators: { paginationMethods: paginationCreator },
+})
+
+const paginationSlice = createAppSlice({
+  name: 'pagination',
+  initialState: { page: 0, loading: false },
+  reducers: (create) => ({
+    ...create.paginationMethods(),
+    toggleLoading: create.reducer((state) => {
+      state.loading = !state.loading
+    }),
+  }),
+})
+
+const { prevPage, nextPage, goToPage, toggleLoading } = paginationSlice.actions
+```
+
+A creator could return a mix of reducer definitions for itself and other creators to handle:
+
+```ts no-transpile
+const historyCreatorType = Symbol()
+
+interface PatchesState {
+  undo: Patch[]
+  redo: Patch[]
+}
+
+interface HistoryState<T> {
+  past: PatchesState[]
+  present: T
+  future: PatchesState[]
+}
+
+declare module '@reduxjs/toolkit' {
+  export interface SliceReducerCreators<
+    State,
+    CaseReducers extends CreatorCaseReducers<State>,
+    Name extends string,
+  > {
+    [historyCreatorType]: ReducerCreatorEntry<
+      // make sure the creator is only called when state is compatibleState extends HistoryState<unknown>
+      State extends HistoryState<any>
+        ? (this: ReducerCreators<State>) => {
+            undo: CaseReducerDefinition<State, PayloadAction>
+            redo: CaseReducerDefinition<State, PayloadAction>
+            reset: ReducerDefinition<typeof historyCreatorType> & {
+              type: 'reset'
+            }
+          }
+        : never,
+      {
+        actions: {
+          [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends ReducerDefinition<
+            typeof historyCreatorType
+          >
+            ? CaseReducers[ReducerName] extends { type: 'reset' }
+              ? PayloadActionCreator<void, SliceActionType<Name, ReducerName>>
+              : never
+            : never
+        }
+        caseReducers: {
+          [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends ReducerDefinition<
+            typeof historyCreatorType
+          >
+            ? CaseReducers[ReducerName] extends { type: 'reset' }
+              ? CaseReducer<State, PayloadAction>
+              : never
+            : never
+        }
+      }
+    >
+  }
+}
+
+const historyCreator: ReducerCreator<typeof historyCreatorType> = {
+  type: historyCreatorType,
+  create() {
+    return {
+      undo: this.reducer((state: HistoryState<unknown>) => {
+        const historyEntry = state.past.pop()
+        if (historyEntry) {
+          applyPatches(state, historyEntry.undo)
+          state.future.unshift(historyEntry)
+        }
+      }),
+      redo: this.reducer((state: HistoryState<unknown>) => {
+        const historyEntry = state.future.shift()
+        if (historyEntry) {
+          applyPatches(state, historyEntry.redo)
+          state.past.push(historyEntry)
+        }
+      }),
+      reset: {
+        _reducerDefinitionType: historyCreatorType,
+        type: 'reset',
+      },
+    }
+  },
+  handle(details, definition, context) {
+    if (definition.type !== 'reset') {
+      throw new Error('Unrecognised definition type: ' + definition.type)
+    }
+    // use the normal reducer creator to create a case reducer and action creator
+    const resetReducer = () => context.getInitialState()
+    reducerCreator.handle(details, reducerCreator.create(resetReducer), context)
+  },
+}
+
+const createAppSlice = buildCreateSlice({
+  creators: { historyMethods: historyCreator },
+})
+
+function getInitialHistoryState<T>(initialState: T): HistoryState<T> {
+  return {
+    past: [],
+    present: initialState,
+    future: [],
+  }
+}
+
+const postSliceWithHistory = createAppSlice({
+  name: 'post',
+  initialState: getInitialHistoryState({ title: '' }),
+  reducers: (create) => ({
+    ...create.historyMethods(),
+  }),
+})
+
+const { undo, redo, reset } = postSliceWithHistory.actions
+```
+
+##### Other
+
+A creator doesn't have to return any reducer definitions, it could be any sort of utility for defining reducers.
+
+Following on from the `HistoryState` example above, it would be useful to make some sort of `undoable` utility to wrap reducers in logic which automatically updates the history of the slice.
+
+Fortunately, this is possible with a creator:
+
+```ts no-transpile
+const undoableCreatorType = Symbol()
+
+interface UndoableMeta {
+  undoable?: boolean
+}
+
+declare module '@reduxjs/toolkit' {
+  export interface SliceReducerCreators<
+    State,
+    CaseReducers extends CreatorCaseReducers<State>,
+    Name extends string,
+  > {
+    [undoableCreatorType]: ReducerCreatorEntry<
+      State extends HistoryState<infer Data>
+        ? {
+            <A extends Action & { meta?: UndoableMeta }>(
+              reducer: CaseReducer<Data, A>,
+            ): CaseReducer<State, A>
+            withoutPayload(options?: UndoableMeta): {
+              payload: undefined
+              meta: UndoableMeta | undefined
+            }
+            withPayload<Payload>(
+              payload: Payload,
+              options?: UndoableMeta,
+            ): { payload: Payload; meta: UndoableMeta | undefined }
+          }
+        : never
+    >
+  }
+}
+
+const undoableCreator: ReducerCreator<typeof undoableCreatorType> = {
+  type: undoableCreatorType,
+  create: Object.assign(
+    function makeUndoable<A extends Action & { meta?: UndoableOptions }>(
+      reducer: CaseReducer<any, A>,
+    ): CaseReducer<HistoryState<any>, A> {
+      return (state, action) => {
+        const [nextState, redoPatch, undoPatch] = produceWithPatches(
+          state,
+          (draft) => {
+            const result = reducer(draft.present, action)
+            if (typeof result !== 'undefined') {
+              draft.present = result
+            }
+          },
+        )
+        let finalState = nextState
+        const undoable = action.meta?.undoable ?? true
+        if (undoable) {
+          finalState = createNextState(finalState, (draft) => {
+            draft.past.push({
+              undo: undoPatch,
+              redo: redoPatch,
+            })
+            draft.future = []
+          })
+        }
+        return finalState
+      }
+    },
+    {
+      withoutPayload() {
+        return (options?: UndoableOptions) => ({
+          payload: undefined,
+          meta: options,
+        })
+      },
+      withPayload<P>() {
+        return (
+          ...[payload, options]: IfMaybeUndefined<
+            P,
+            [payload?: P, options?: UndoableOptions],
+            [payload: P, options?: UndoableOptions]
+          >
+        ) => ({ payload: payload as P, meta: options })
+      },
+    },
+  ),
+}
+
+const createAppSlice = buildCreateSlice({
+  creators: { historyMethods: historyCreator, undoable: undoableCreator },
+})
+
+const postSliceWithHistory = createAppSlice({
+  name: 'post',
+  initialState: getInitialHistoryState({ title: '', pinned: false }),
+  reducers: (create) => ({
+    ...create.historyMethods(),
+    updateTitle: create.preparedReducer(
+      create.undoable.withPayload<string>(),
+      create.undoable((state, action) => {
+        state.title = action.payload
+      }),
+    ),
+    togglePinned: create.preparedReducer(
+      create.undoable.withoutPayload(),
+      create.undoable((state, action) => {
+        state.pinned = !state.pinned
+      }),
+    ),
+  }),
+})
+
+const { undo, redo, reset, updateTitle, togglePinned } =
+  postSliceWithHistory.actions
+```

From 2ce8b30ff20e5c321b5aca0080b66f47689e9a2a Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Thu, 22 Feb 2024 23:03:47 +0000
Subject: [PATCH 137/178] take titles down a step

---
 docs/api/createSlice.mdx             | 16 ++++-----
 docs/usage/custom-slice-creators.mdx | 52 +++++++++++++---------------
 2 files changed, 33 insertions(+), 35 deletions(-)

diff --git a/docs/api/createSlice.mdx b/docs/api/createSlice.mdx
index dba5ee9576..bd3afde05b 100644
--- a/docs/api/createSlice.mdx
+++ b/docs/api/createSlice.mdx
@@ -183,13 +183,13 @@ const todosSlice = createAppSlice({
         return (await res.json()) as Item
       },
       {
-        pending: (state) => {
+        pending(state) {
           state.loading = true
         },
-        rejected: (state, action) => {
+        rejected(state, action) {
           state.loading = false
         },
-        fulfilled: (state, action) => {
+        fulfilled(state, action) {
           state.loading = false
           state.todos.push(action.payload)
         },
@@ -284,16 +284,16 @@ create.asyncThunk(
     return (await res.json()) as Item
   },
   {
-    pending: (state) => {
+    pending(state) {
       state.loading = true
     },
-    rejected: (state, action) => {
+    rejected(state, action)  {
       state.error = action.payload ?? action.error
     },
-    fulfilled: (state, action) => {
+    fulfilled(state, action)  {
       state.todos.push(action.payload)
     },
-    settled: (state, action) => {
+    settled(state, action)  {
       state.loading = false
     }
     options: {
@@ -673,7 +673,7 @@ store.dispatch(user.actions.setUserName('eric'))
 
 `buildCreateSlice` allows you to create a custom version of `createSlice` with some configuration.
 
-Currently, this is only used for [slice creators](#slice-reducer-creators).
+Currently, this is only used for [slice creators](../usage/custom-slice-creators).
 
 ### Parameters
 
diff --git a/docs/usage/custom-slice-creators.mdx b/docs/usage/custom-slice-creators.mdx
index 4d534afbb5..543ff3ec81 100644
--- a/docs/usage/custom-slice-creators.mdx
+++ b/docs/usage/custom-slice-creators.mdx
@@ -7,8 +7,6 @@ hide_title: true
 
 # Custom Slice Creators
 
-## Slice creators
-
 Redux Toolkit 2.0 introduces the concept of "slice creators", which allow you to define reusable logic for creating slices.
 
 These "creators" have the capability to:
@@ -46,7 +44,7 @@ const { undo, redo, reset, updateTitle, togglePinned } =
   postSliceWithHistory.actions
 ```
 
-### The `reducers` "creator callback" notation
+## The `reducers` "creator callback" notation
 
 In order to use slice creators, `reducers` becomes a callback, which receives a `create` object. This `create` object contains a couple of [inbuilt creators](#rtk-creators), along with any creators passed to [`buildCreateSlice`](#buildcreateslice).
 
@@ -111,11 +109,11 @@ const todosSlice = createAppSlice({
 export const { addTodo, deleteTodo, fetchTodo } = todosSlice.actions
 ```
 
-#### RTK Creators
+### RTK Creators
 
 These creators come built into RTK, and are always available on the `create` object passed to the `reducers` callback.
 
-##### `create.reducer`
+#### `create.reducer`
 
 A standard slice case reducer. Creates an action creator with the same name as the reducer.
 
@@ -135,7 +133,7 @@ The [creator definition](#creator-definitions) for `create.reducer` is exported
 
 :::
 
-##### `create.preparedReducer`
+#### `create.preparedReducer`
 
 A [prepared](#customizing-generated-action-creators) reducer, to customize the action creator. Creates a prepared action creator with the same name as the reducer.
 
@@ -164,7 +162,7 @@ The [creator definition](#creator-definitions) for `create.preparedReducer` is e
 
 :::
 
-#### Optional RTK Creators
+### Optional RTK Creators
 
 These creators are not included in the default `create` object, but can be added by passing them to [`buildCreateSlice`](#buildcreateslice).
 
@@ -231,7 +229,7 @@ const createAppSlice = buildCreateSlice({
 
 :::
 
-##### `create.asyncThunk` (`asyncThunkCreator`)
+#### `create.asyncThunk` (`asyncThunkCreator`)
 
 Creates an async thunk and adds any provided case reducers for lifecycle actions.
 
@@ -326,13 +324,13 @@ reducers: (create) => {
 
 :::
 
-### Writing your own creators
+## Writing your own creators
 
 In version v2.3.0, we introduced a system for including your own creators.
 
 The below documentation will cover how to write your own creators, and how to use them with `createSlice`.
 
-#### Reducer definitions
+### Reducer definitions
 
 A reducer definition is an object (or function) with a `_reducerDefinitionType` property indicating which creator should handle it. Other than this property, it is entirely up to the creator what this definition object can look like.
 
@@ -363,7 +361,7 @@ const definitions = {
 
 Typically a creator will return a [single reducer definition](#single-definitions), but it could return an object of [multiple definitions](#multiple-definitions) to be spread into the final object, or [something else entirely](#other)!
 
-#### Creator definitions
+### Creator definitions
 
 A creator definition contains the actual runtime logic for that creator. It's an object with a `type` property, a `create` method, and an optional `handle` method.
 
@@ -413,7 +411,7 @@ const reducerCreator: ReducerCreator<typeof reducerCreatorType> = {
 }
 ```
 
-##### `create`
+#### `create`
 
 The `create` method is the function that will be attached to the `create` object, before it's passed to the `reducers` callback.
 
@@ -421,7 +419,7 @@ Because it's a function, the `this` value will be the final `create` object when
 
 See the [Further examples](#further-examples) section for some examples of these.
 
-#### `handle`
+### `handle`
 
 The `handle` callback of a creator will be called for any reducer definitions with a matching `_reducerDefinitionType` property.
 
@@ -438,7 +436,7 @@ The reducer details object has two properties:
 
 The context object includes:
 
-##### `addCase`
+#### `addCase`
 
 The same as [`addCase`](./createReducer#builderaddcase) for `createReducer` and `extraReducers`. Adds a case reducer for a given action type, and can receive an action type string or an action creator with a `.type` property.
 
@@ -447,7 +445,7 @@ const action = createAction(type)
 context.addCase(action, reducer)
 ```
 
-##### `addMatcher`
+#### `addMatcher`
 
 The same as [`addMatcher`](./createReducer#builderaddmatcher) for `createReducer` and `extraReducers`. Adds a case reducer which will be called when a given matcher returns true.
 
@@ -456,7 +454,7 @@ const matcher = isAnyOf(action, action2)
 context.addMatcher(matcher, reducer)
 ```
 
-##### `exposeAction`
+#### `exposeAction`
 
 Attaches a value to `slice.actions`. Receives the key to be set under (typically `reducerName`) and the value to be set.
 
@@ -465,7 +463,7 @@ const action = createAction(type)
 context.exposeAction(reducerName, action)
 ```
 
-##### `exposeCaseReducer`
+#### `exposeCaseReducer`
 
 Attaches a value to `slice.caseReducers`. Receives the key to be set under (typically `reducerName`) and the value to be set.
 
@@ -473,7 +471,7 @@ Attaches a value to `slice.caseReducers`. Receives the key to be set under (typi
 context.exposeCaseReducer(reducerName, reducer)
 ```
 
-##### `getInitialState`
+#### `getInitialState`
 
 Returns the initial state value for the slice. If a lazy state initializer has been provided, it will be called and a fresh value returned.
 
@@ -486,7 +484,7 @@ context
   .exposeCaseReducer(reducerName, resetReducer)
 ```
 
-#### Typescript
+### Typescript
 
 The Typescript system for custom slice creators uses a "creator registry" system similar to the module system for [RTK Query](/rtk-query/usage/customizing-create-api#creating-your-own-module).
 
@@ -516,7 +514,7 @@ The type parameters for `SliceReducerCreators` are:
 
 The `ReducerCreatorEntry<Create, Exposes>` utility has two type parameters:
 
-##### `Create`
+#### `Create`
 
 The signature of the `create` method of the creator definition.
 
@@ -617,13 +615,13 @@ declare module '@reduxjs/toolkit' {
 
 :::
 
-##### `Exposes` (optional)
+#### `Exposes` (optional)
 
 The second type parameter for `ReducerCreatorEntry` is optional, but should be used if the creator will handle some reducer definitions itself. It indicates what actions and case reducers will be attached to the slice, and is used to determine the final types of `slice.actions` and `slice.caseReducers`.
 
 It should be an object with some of the following properties:
 
-###### `actions`
+##### `actions`
 
 The actions property will typically be a [mapped type](https://www.typescriptlang.org/docs/handbook/2/mapped-types.html) over the `CaseReducers` type parameter, returning what the creator's `handle` would expose when given that definition.
 
@@ -668,7 +666,7 @@ declare module '@reduxjs/toolkit' {
 }
 ```
 
-###### `caseReducers`
+##### `caseReducers`
 
 Similar to `actions`, except for `slice.caseReducers`.
 
@@ -718,7 +716,7 @@ declare module '@reduxjs/toolkit' {
 }
 ```
 
-#### Further examples
+### Further examples
 
 This section will cover in depth examples, for potential applications with hopefully applicable lessons to other use cases.
 
@@ -743,7 +741,7 @@ export const createAppSlice = buildCreateSlice({
 
 :::
 
-##### Single definitions
+#### Single definitions
 
 Commonly, a creator will return a single reducer definition, to be handled by either itself or another creator.
 
@@ -900,7 +898,7 @@ const { showToast } = toastSlice.actions
 toastSlice.caseReducers.showToast.hidden({}, showToast.hidden('id'))
 ```
 
-##### Multiple definitions
+#### Multiple definitions
 
 A creator could also return multiple definitions, which would then be spread into the final definitions object. This is a more composable alternative to the [wrapping `createSlice`](usage/usage-with-typescript#wrapping-createslice) approach, as you could call multiple creators as needed.
 
@@ -1081,7 +1079,7 @@ const postSliceWithHistory = createAppSlice({
 const { undo, redo, reset } = postSliceWithHistory.actions
 ```
 
-##### Other
+#### Other
 
 A creator doesn't have to return any reducer definitions, it could be any sort of utility for defining reducers.
 

From 7e60c5add762d19d35fd2d6d7d6b2ee1f49c0a7b Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Thu, 22 Feb 2024 23:49:07 +0000
Subject: [PATCH 138/178] a word

---
 docs/usage/custom-slice-creators.mdx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/usage/custom-slice-creators.mdx b/docs/usage/custom-slice-creators.mdx
index 543ff3ec81..9651a95663 100644
--- a/docs/usage/custom-slice-creators.mdx
+++ b/docs/usage/custom-slice-creators.mdx
@@ -7,7 +7,7 @@ hide_title: true
 
 # Custom Slice Creators
 
-Redux Toolkit 2.0 introduces the concept of "slice creators", which allow you to define reusable logic for creating slices.
+Redux Toolkit 2.0 introduces the concept of "slice creators", which allow you to define reusable logic for creating slice reducers.
 
 These "creators" have the capability to:
 

From e31060426e57d28af12fbccefd465a1ee27c2988 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Fri, 23 Feb 2024 17:51:24 +0000
Subject: [PATCH 139/178] fix links

---
 docs/usage/custom-slice-creators.mdx | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/docs/usage/custom-slice-creators.mdx b/docs/usage/custom-slice-creators.mdx
index 9651a95663..1d089e48c8 100644
--- a/docs/usage/custom-slice-creators.mdx
+++ b/docs/usage/custom-slice-creators.mdx
@@ -46,7 +46,7 @@ const { undo, redo, reset, updateTitle, togglePinned } =
 
 ## The `reducers` "creator callback" notation
 
-In order to use slice creators, `reducers` becomes a callback, which receives a `create` object. This `create` object contains a couple of [inbuilt creators](#rtk-creators), along with any creators passed to [`buildCreateSlice`](#buildcreateslice).
+In order to use slice creators, `reducers` becomes a callback, which receives a `create` object. This `create` object contains a couple of [inbuilt creators](#rtk-creators), along with any creators passed to [`buildCreateSlice`](../api/createSlice#buildcreateslice).
 
 ```ts title="Creator callback for reducers"
 import { buildCreateSlice, asyncThunkCreator, nanoid } from '@reduxjs/toolkit'
@@ -135,11 +135,11 @@ The [creator definition](#creator-definitions) for `create.reducer` is exported
 
 #### `create.preparedReducer`
 
-A [prepared](#customizing-generated-action-creators) reducer, to customize the action creator. Creates a prepared action creator with the same name as the reducer.
+A [prepared](../api/createSlice#customizing-generated-action-creators) reducer, to customize the action creator. Creates a prepared action creator with the same name as the reducer.
 
 **Parameters**
 
-- **prepareAction** The [`prepare callback`](./createAction#using-prepare-callbacks-to-customize-action-contents).
+- **prepareAction** The [`prepare callback`](../api/createAction#using-prepare-callbacks-to-customize-action-contents).
 - **reducer** The slice case reducer to use.
 
 The action passed to the case reducer will be inferred from the prepare callback's return.
@@ -164,7 +164,7 @@ The [creator definition](#creator-definitions) for `create.preparedReducer` is e
 
 ### Optional RTK Creators
 
-These creators are not included in the default `create` object, but can be added by passing them to [`buildCreateSlice`](#buildcreateslice).
+These creators are not included in the default `create` object, but can be added by passing them to [`buildCreateSlice`](../api/createSlice#buildcreateslice).
 
 The name the creator is available under is based on the key used when calling `buildCreateSlice`. For example, to use `create.asyncThunk`:
 
@@ -235,14 +235,14 @@ Creates an async thunk and adds any provided case reducers for lifecycle actions
 
 **Parameters**
 
-- **payloadCreator** The thunk [payload creator](./createAsyncThunk#payloadcreator).
+- **payloadCreator** The thunk [payload creator](../api/createAsyncThunk#payloadcreator).
 - **config** The configuration object. (optional)
 
-The configuration object can contain case reducers for each of the [lifecycle actions](./createAsyncThunk#promise-lifecycle-actions) (`pending`, `fulfilled`, and `rejected`), as well as a `settled` reducer that will run for both fulfilled and rejected actions (note that this will run _after_ any provided `fulfilled`/`rejected` reducers. Conceptually it can be thought of like a `finally` block.).
+The configuration object can contain case reducers for each of the [lifecycle actions](../api/createAsyncThunk#promise-lifecycle-actions) (`pending`, `fulfilled`, and `rejected`), as well as a `settled` reducer that will run for both fulfilled and rejected actions (note that this will run _after_ any provided `fulfilled`/`rejected` reducers. Conceptually it can be thought of like a `finally` block.).
 
 Each case reducer will be attached to the slice's `caseReducers` object, e.g. `slice.caseReducers.fetchTodo.fulfilled`.
 
-The configuration object can also contain [`options`](./createAsyncThunk#options).
+The configuration object can also contain [`options`](../api/createAsyncThunk#options).
 
 ```ts no-transpile
 create.asyncThunk(
@@ -272,7 +272,7 @@ create.asyncThunk(
 
 :::note
 
-Typing for `create.asyncThunk` works in the same way as [`createAsyncThunk`](../usage/usage-with-typescript#createasyncthunk), with one key difference.
+Typing for `create.asyncThunk` works in the same way as [`createAsyncThunk`](./usage-with-typescript#createasyncthunk), with one key difference.
 
 A type for `state` and/or `dispatch` _cannot_ be provided as part of the `ThunkApiConfig`, as this would cause circular types.
 
@@ -299,7 +299,7 @@ create.asyncThunk<Todo, string, { rejectValue: { error: string } }>(
 )
 ```
 
-For common thunk API configuration options, a [`withTypes` helper](../usage/usage-with-typescript#defining-a-pre-typed-createasyncthunk) is provided:
+For common thunk API configuration options, a [`withTypes` helper](./usage-with-typescript#defining-a-pre-typed-createasyncthunk) is provided:
 
 ```ts no-transpile
 reducers: (create) => {
@@ -365,7 +365,7 @@ Typically a creator will return a [single reducer definition](#single-definition
 
 A creator definition contains the actual runtime logic for that creator. It's an object with a `type` property, a `create` method, and an optional `handle` method.
 
-It's passed to [`buildCreateSlice`](#buildcreateslice) as part of the `creators` object, and the name used when calling `buildCreateSlice` will be the key the creator is nested under in the `create` object.
+It's passed to [`buildCreateSlice`](../api/createSlice#buildcreateslice) as part of the `creators` object, and the name used when calling `buildCreateSlice` will be the key the creator is nested under in the `create` object.
 
 ```ts no-transpile
 import { buildCreateSlice } from '@reduxjs/toolkit'
@@ -438,7 +438,7 @@ The context object includes:
 
 #### `addCase`
 
-The same as [`addCase`](./createReducer#builderaddcase) for `createReducer` and `extraReducers`. Adds a case reducer for a given action type, and can receive an action type string or an action creator with a `.type` property.
+The same as [`addCase`](../api/createReducer#builderaddcase) for `createReducer` and `extraReducers`. Adds a case reducer for a given action type, and can receive an action type string or an action creator with a `.type` property.
 
 ```ts no-transpile
 const action = createAction(type)
@@ -447,7 +447,7 @@ context.addCase(action, reducer)
 
 #### `addMatcher`
 
-The same as [`addMatcher`](./createReducer#builderaddmatcher) for `createReducer` and `extraReducers`. Adds a case reducer which will be called when a given matcher returns true.
+The same as [`addMatcher`](../api/createReducer#builderaddmatcher) for `createReducer` and `extraReducers`. Adds a case reducer which will be called when a given matcher returns true.
 
 ```ts no-transpile
 const matcher = isAnyOf(action, action2)
@@ -510,7 +510,7 @@ The type parameters for `SliceReducerCreators` are:
 
 - `State` - The state type used by the slice.
 - `CaseReducers` - The case reducer definitions returned by the creator callback.
-- `Name` - The [`name`](#name) used by the slice.
+- `Name` - The [`name`](../api/createSlice#name) used by the slice.
 
 The `ReducerCreatorEntry<Create, Exposes>` utility has two type parameters:
 
@@ -900,7 +900,7 @@ toastSlice.caseReducers.showToast.hidden({}, showToast.hidden('id'))
 
 #### Multiple definitions
 
-A creator could also return multiple definitions, which would then be spread into the final definitions object. This is a more composable alternative to the [wrapping `createSlice`](usage/usage-with-typescript#wrapping-createslice) approach, as you could call multiple creators as needed.
+A creator could also return multiple definitions, which would then be spread into the final definitions object. This is a more composable alternative to the [wrapping `createSlice`](./usage-with-typescript#wrapping-createslice) approach, as you could call multiple creators as needed.
 
 One example could be returning some pagination related reducers.
 

From 7148e8c265f4f962807a021adad40f96997df9fd Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Tue, 12 Mar 2024 02:17:15 +0000
Subject: [PATCH 140/178] omit any from extracted definitions

---
 packages/toolkit/src/createSlice.ts | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 75d2a217b6..8b0d388e20 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -21,7 +21,7 @@ import type {
 import { createReducer, makeGetInitialState } from './createReducer'
 import type { ActionReducerMapBuilder, TypedActionCreator } from './mapBuilders'
 import { executeReducerBuilderCallback } from './mapBuilders'
-import type { Id, TypeGuard, UnionToIntersection } from './tsHelpers'
+import type { CastAny, Id, TypeGuard, UnionToIntersection } from './tsHelpers'
 import type { InjectConfig } from './combineSlices'
 import { emplace } from './utils'
 import { DistributiveOmit } from 'react-redux'
@@ -275,7 +275,7 @@ type RecursiveExtractDefinition<
   Definitions,
   Type extends RegisteredReducerType,
 > =
-  | Extract<Definitions, ReducerDefinition<Type>>
+  | CastAny<Extract<Definitions, ReducerDefinition<Type>>, never>
   | (Definitions extends object
       ? {
           [K in keyof Definitions]-?: RecursiveExtractDefinition<

From b05b9a757b99790823c9d2ba240860ddf0de83e8 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Tue, 12 Mar 2024 02:20:21 +0000
Subject: [PATCH 141/178] cast more anys

---
 packages/toolkit/src/createSlice.ts | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 8b0d388e20..20cb444ec2 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -274,8 +274,8 @@ export interface ReducerDetails {
 type RecursiveExtractDefinition<
   Definitions,
   Type extends RegisteredReducerType,
-> =
-  | CastAny<Extract<Definitions, ReducerDefinition<Type>>, never>
+> = CastAny<
+  | Extract<Definitions, ReducerDefinition<Type>>
   | (Definitions extends object
       ? {
           [K in keyof Definitions]-?: RecursiveExtractDefinition<
@@ -283,7 +283,9 @@ type RecursiveExtractDefinition<
             Type
           >
         }[keyof Definitions]
-      : never)
+      : never),
+  never
+>
 
 type ReducerDefinitionsForType<Type extends RegisteredReducerType> = {
   [CreatorType in RegisteredReducerType]:

From 94937b1e6185e55151bd7e21f602e52e1c8bf7c7 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Fri, 5 Apr 2024 11:59:36 +0100
Subject: [PATCH 142/178] rename context types

---
 packages/toolkit/src/createSlice.ts           | 20 +++++++++----------
 packages/toolkit/src/index.ts                 |  2 +-
 .../toolkit/src/tests/createSlice.test-d.ts   |  4 ++--
 3 files changed, 13 insertions(+), 13 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 20cb444ec2..447006583f 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -182,7 +182,7 @@ export type ReducerCreators<
     : Name]: SliceReducerCreators<State, any, any>[CreatorMap[Name]]['create']
 }
 
-interface ReducerHandlingContext<State> {
+interface InternalReducerHandlingContext<State> {
   sliceCaseReducersByType: Record<string, CaseReducer<State, any>>
   sliceMatchers: ActionMatcherDescriptionCollection<State>
 
@@ -190,7 +190,7 @@ interface ReducerHandlingContext<State> {
   actionCreators: Record<string, any>
 }
 
-export interface ReducerHandlingContextMethods<State> {
+export interface ReducerHandlingContext<State> {
   /**
    * Adds a case reducer to handle a single action type.
    * @param actionCreator - Either a plain action type string, or an action creator generated by [`createAction`](./createAction) that can be used to determine the action type.
@@ -199,7 +199,7 @@ export interface ReducerHandlingContextMethods<State> {
   addCase<ActionCreator extends TypedActionCreator<string>>(
     actionCreator: ActionCreator,
     reducer: CaseReducer<State, ReturnType<ActionCreator>>,
-  ): ReducerHandlingContextMethods<State>
+  ): ReducerHandlingContext<State>
   /**
    * Adds a case reducer to handle a single action type.
    * @param actionCreator - Either a plain action type string, or an action creator generated by [`createAction`](./createAction) that can be used to determine the action type.
@@ -208,7 +208,7 @@ export interface ReducerHandlingContextMethods<State> {
   addCase<Type extends string, A extends Action<Type>>(
     type: Type,
     reducer: CaseReducer<State, A>,
-  ): ReducerHandlingContextMethods<State>
+  ): ReducerHandlingContext<State>
 
   /**
    * Allows you to match incoming actions against your own filter function instead of only the `action.type` property.
@@ -224,7 +224,7 @@ export interface ReducerHandlingContextMethods<State> {
   addMatcher<A>(
     matcher: TypeGuard<A>,
     reducer: CaseReducer<State, A extends Action ? A : A & Action>,
-  ): ReducerHandlingContextMethods<State>
+  ): ReducerHandlingContext<State>
   /**
    * Add an action to be exposed under the final `slice.actions` key.
    * @param name The key to be exposed as.
@@ -239,7 +239,7 @@ export interface ReducerHandlingContextMethods<State> {
   exposeAction(
     name: string,
     actionCreator: unknown,
-  ): ReducerHandlingContextMethods<State>
+  ): ReducerHandlingContext<State>
   /**
    * Add a case reducer to be exposed under the final `slice.caseReducers` key.
    * @param name The key to be exposed as.
@@ -254,7 +254,7 @@ export interface ReducerHandlingContextMethods<State> {
   exposeCaseReducer(
     name: string,
     reducer: unknown,
-  ): ReducerHandlingContextMethods<State>
+  ): ReducerHandlingContext<State>
   /**
    * Provides access to the initial state value given to the slice.
    * If a lazy state initializer was provided, it will be called and a fresh value returned.
@@ -319,7 +319,7 @@ export type ReducerCreator<Type extends RegisteredReducerType> = {
       handle<State>(
         details: ReducerDetails,
         definition: ReducerDefinitionsForType<Type>,
-        context: ReducerHandlingContextMethods<State>,
+        context: ReducerHandlingContext<State>,
       ): void
     })
 
@@ -892,14 +892,14 @@ export function buildCreateSlice<
 
     const getInitialState = makeGetInitialState(options.initialState)
 
-    const context: ReducerHandlingContext<State> = {
+    const context: InternalReducerHandlingContext<State> = {
       sliceCaseReducersByName: {},
       sliceCaseReducersByType: {},
       actionCreators: {},
       sliceMatchers: [],
     }
 
-    const contextMethods: ReducerHandlingContextMethods<State> = {
+    const contextMethods: ReducerHandlingContext<State> = {
       addCase(
         typeOrActionCreator: string | TypedActionCreator<any>,
         reducer: CaseReducer<State>,
diff --git a/packages/toolkit/src/index.ts b/packages/toolkit/src/index.ts
index 32747dcbaa..d3952258fe 100644
--- a/packages/toolkit/src/index.ts
+++ b/packages/toolkit/src/index.ts
@@ -87,7 +87,7 @@ export type {
   ReducerCreatorEntry,
   ReducerCreator,
   ReducerDetails,
-  ReducerHandlingContextMethods,
+  ReducerHandlingContext,
   SliceActionType,
   CaseReducerDefinition,
   PreparedCaseReducerDefinition,
diff --git a/packages/toolkit/src/tests/createSlice.test-d.ts b/packages/toolkit/src/tests/createSlice.test-d.ts
index a586f875d8..94e756bc6c 100644
--- a/packages/toolkit/src/tests/createSlice.test-d.ts
+++ b/packages/toolkit/src/tests/createSlice.test-d.ts
@@ -14,7 +14,7 @@ import type {
   ReducerCreatorEntry,
   ReducerCreators,
   ReducerDefinition,
-  ReducerHandlingContextMethods,
+  ReducerHandlingContext,
   SliceActionType,
   SliceCaseReducers,
   ThunkAction,
@@ -814,7 +814,7 @@ describe('type tests', () => {
           }
         }
         Object.assign(openToast, { toastOpened, toastClosed })
-        ;(context as any as ReducerHandlingContextMethods<ToastState>)
+        ;(context as any as ReducerHandlingContext<ToastState>)
           .addCase(toastOpened, (state, { payload: { message, id } }) => {
             state.toasts[id] = { message }
           })

From 5403d801d5b0b6d0db2d8f48a5c2c263e0d2c90f Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Fri, 5 Apr 2024 12:14:16 +0100
Subject: [PATCH 143/178] remove first argument for exposeAction and
 exposeCaseReducer, and always use reducerName

---
 docs/usage/custom-slice-creators.mdx          |  30 ++--
 packages/toolkit/src/asyncThunkCreator.ts     |   6 +-
 packages/toolkit/src/createSlice.ts           | 141 ++++++++++--------
 .../toolkit/src/tests/createSlice.test-d.ts   |   2 +-
 .../toolkit/src/tests/createSlice.test.ts     |   4 +-
 5 files changed, 98 insertions(+), 85 deletions(-)

diff --git a/docs/usage/custom-slice-creators.mdx b/docs/usage/custom-slice-creators.mdx
index 1d089e48c8..1241ac172d 100644
--- a/docs/usage/custom-slice-creators.mdx
+++ b/docs/usage/custom-slice-creators.mdx
@@ -400,13 +400,13 @@ const reducerCreator: ReducerCreator<typeof reducerCreatorType> = {
       reducer,
     }
   },
-  handle({ reducerName, type }, definition, context) {
+  handle({ type }, definition, context) {
     const { reducer } = definition
     const actionCreator = createAction(type)
     context
       .addCase(actionCreator, reducer)
-      .exposeAction(reducerName, actionCreator)
-      .exposeCaseReducer(reducerName, reducer)
+      .exposeAction(actionCreator)
+      .exposeCaseReducer(reducer)
   },
 }
 ```
@@ -456,19 +456,19 @@ context.addMatcher(matcher, reducer)
 
 #### `exposeAction`
 
-Attaches a value to `slice.actions`. Receives the key to be set under (typically `reducerName`) and the value to be set.
+Attaches a value to `slice.actions[reducerName]`.
 
 ```ts no-transpile
 const action = createAction(type)
-context.exposeAction(reducerName, action)
+context.exposeAction(action)
 ```
 
 #### `exposeCaseReducer`
 
-Attaches a value to `slice.caseReducers`. Receives the key to be set under (typically `reducerName`) and the value to be set.
+Attaches a value to `slice.caseReducers[reducerName]`.
 
 ```ts no-transpile
-context.exposeCaseReducer(reducerName, reducer)
+context.exposeCaseReducer(reducer)
 ```
 
 #### `getInitialState`
@@ -480,8 +480,8 @@ const resetAction = createAction(type)
 const resetReducer = () => context.getInitialState()
 context
   .addCase(resetAction, resetReducer)
-  .exposeAction(reducerName, resetAction)
-  .exposeCaseReducer(reducerName, resetReducer)
+  .exposeAction(resetAction)
+  .exposeCaseReducer(resetReducer)
 ```
 
 ### Typescript
@@ -820,7 +820,7 @@ const toastCreator: ReducerCreator<typeof toastCreatorType> = {
     }
   },
   // handle the reducer definition
-  handle({ reducerName, type }, definition, context) {
+  handle({ type }, definition, context) {
     // make the action creators
     const shown = createAction(type + '/shown', (toast: Toast, id: string) => ({
       payload: toast,
@@ -859,12 +859,10 @@ const toastCreator: ReducerCreator<typeof toastCreatorType> = {
     }
 
     // expose the thunk creator as `slice.actions[reducerName]` and the case reducers as `slice.caseReducers[reducerName]["shown" | "hidden"]`
-    context
-      .exposeAction(reducerName, thunkCreator)
-      .exposeCaseReducer(reducerName, {
-        shown: definition.shown || noop,
-        hidden: definition.hidden || noop,
-      })
+    context.exposeAction(thunkCreator).exposeCaseReducer({
+      shown: definition.shown || noop,
+      hidden: definition.hidden || noop,
+    })
   },
 }
 
diff --git a/packages/toolkit/src/asyncThunkCreator.ts b/packages/toolkit/src/asyncThunkCreator.ts
index dfefb82719..d0ae93ed75 100644
--- a/packages/toolkit/src/asyncThunkCreator.ts
+++ b/packages/toolkit/src/asyncThunkCreator.ts
@@ -125,11 +125,11 @@ export const asyncThunkCreator: ReducerCreator<ReducerType.asyncThunk> = {
     asyncThunk.withTypes = () => asyncThunk
     return asyncThunk as AsyncThunkCreator<any>
   })(),
-  handle({ type, reducerName }, definition, context) {
+  handle({ type }, definition, context) {
     const { payloadCreator, fulfilled, pending, rejected, settled, options } =
       definition
     const thunk = createAsyncThunk(type, payloadCreator, options as any)
-    context.exposeAction(reducerName, thunk)
+    context.exposeAction(thunk)
 
     if (fulfilled) {
       context.addCase(thunk.fulfilled, fulfilled)
@@ -144,7 +144,7 @@ export const asyncThunkCreator: ReducerCreator<ReducerType.asyncThunk> = {
       context.addMatcher(thunk.settled, settled)
     }
 
-    context.exposeCaseReducer(reducerName, {
+    context.exposeCaseReducer({
       fulfilled: fulfilled || noop,
       pending: pending || noop,
       rejected: rejected || noop,
diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 447006583f..089b4d287c 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -226,35 +226,27 @@ export interface ReducerHandlingContext<State> {
     reducer: CaseReducer<State, A extends Action ? A : A & Action>,
   ): ReducerHandlingContext<State>
   /**
-   * Add an action to be exposed under the final `slice.actions` key.
-   * @param name The key to be exposed as.
+   * Add an action to be exposed under the final `slice.actions[reducerName]` key.
    * @param actionCreator The action to expose.
    * @example
-   * context.exposeAction("addPost", createAction<Post>("addPost"));
+   * context.exposeAction(createAction<Post>(type));
    *
    * export const { addPost } = slice.actions
    *
    * dispatch(addPost(post))
    */
-  exposeAction(
-    name: string,
-    actionCreator: unknown,
-  ): ReducerHandlingContext<State>
+  exposeAction(actionCreator: unknown): ReducerHandlingContext<State>
   /**
-   * Add a case reducer to be exposed under the final `slice.caseReducers` key.
-   * @param name The key to be exposed as.
+   * Add a case reducer to be exposed under the final `slice.caseReducers[reducerName]` key.
    * @param reducer The reducer to expose.
    * @example
-   * context.exposeCaseReducer("addPost", (state, action: PayloadAction<Post>) => {
+   * context.exposeCaseReducer((state, action: PayloadAction<Post>) => {
    *   state.push(action.payload)
    * })
    *
    * slice.caseReducers.addPost([], addPost(post))
    */
-  exposeCaseReducer(
-    name: string,
-    reducer: unknown,
-  ): ReducerHandlingContext<State>
+  exposeCaseReducer(reducer: unknown): ReducerHandlingContext<State>
   /**
    * Provides access to the initial state value given to the slice.
    * If a lazy state initializer was provided, it will be called and a fresh value returned.
@@ -756,11 +748,11 @@ export const reducerCreator: ReducerCreator<ReducerType.reducer> = {
       } as const,
     )
   },
-  handle({ type, reducerName }, reducer, context) {
+  handle({ type }, reducer, context) {
     context
       .addCase(type, reducer as any)
-      .exposeCaseReducer(reducerName, reducer)
-      .exposeAction(reducerName, createAction(type))
+      .exposeCaseReducer(reducer)
+      .exposeAction(createAction(type))
   },
 }
 
@@ -774,11 +766,11 @@ export const preparedReducerCreator: ReducerCreator<ReducerType.reducerWithPrepa
         reducer,
       }
     },
-    handle({ type, reducerName }, { prepare, reducer }, context) {
+    handle({ type }, { prepare, reducer }, context) {
       context
         .addCase(type, reducer)
-        .exposeCaseReducer(reducerName, reducer)
-        .exposeAction(reducerName, createAction(type, prepare))
+        .exposeCaseReducer(reducer)
+        .exposeAction(createAction(type, prepare))
     },
   }
 
@@ -892,49 +884,64 @@ export function buildCreateSlice<
 
     const getInitialState = makeGetInitialState(options.initialState)
 
-    const context: InternalReducerHandlingContext<State> = {
+    const internalContext: InternalReducerHandlingContext<State> = {
       sliceCaseReducersByName: {},
       sliceCaseReducersByType: {},
       actionCreators: {},
       sliceMatchers: [],
     }
 
-    const contextMethods: ReducerHandlingContext<State> = {
-      addCase(
-        typeOrActionCreator: string | TypedActionCreator<any>,
-        reducer: CaseReducer<State>,
-      ) {
-        const type =
-          typeof typeOrActionCreator === 'string'
-            ? typeOrActionCreator
-            : typeOrActionCreator.type
-        if (!type) {
-          throw new Error(
-            '`context.addCase` cannot be called with an empty action type',
-          )
-        }
-        if (type in context.sliceCaseReducersByType) {
-          throw new Error(
-            '`context.addCase` cannot be called with two reducers for the same action type: ' +
-              type,
-          )
-        }
-        context.sliceCaseReducersByType[type] = reducer
-        return contextMethods
-      },
-      addMatcher(matcher, reducer) {
-        context.sliceMatchers.push({ matcher, reducer })
-        return contextMethods
-      },
-      exposeAction(name, actionCreator) {
-        context.actionCreators[name] = actionCreator
-        return contextMethods
-      },
-      exposeCaseReducer(name, reducer) {
-        context.sliceCaseReducersByName[name] = reducer
-        return contextMethods
-      },
-      getInitialState,
+    function getContext({ reducerName }: ReducerDetails) {
+      const context: ReducerHandlingContext<State> = {
+        addCase(
+          typeOrActionCreator: string | TypedActionCreator<any>,
+          reducer: CaseReducer<State>,
+        ) {
+          const type =
+            typeof typeOrActionCreator === 'string'
+              ? typeOrActionCreator
+              : typeOrActionCreator.type
+          if (!type) {
+            throw new Error(
+              '`context.addCase` cannot be called with an empty action type',
+            )
+          }
+          if (type in internalContext.sliceCaseReducersByType) {
+            throw new Error(
+              '`context.addCase` cannot be called with two reducers for the same action type: ' +
+                type,
+            )
+          }
+          internalContext.sliceCaseReducersByType[type] = reducer
+          return context
+        },
+        addMatcher(matcher, reducer) {
+          internalContext.sliceMatchers.push({ matcher, reducer })
+          return context
+        },
+        exposeAction(actionCreator) {
+          if (reducerName in internalContext.actionCreators) {
+            throw new Error(
+              'context.exposeAction cannot be called twice for the same reducer definition:' +
+                reducerName,
+            )
+          }
+          internalContext.actionCreators[reducerName] = actionCreator
+          return context
+        },
+        exposeCaseReducer(reducer) {
+          if (reducerName in internalContext.sliceCaseReducersByName) {
+            throw new Error(
+              'context.exposeCaseReducer cannot be called twice for the same reducer definition:' +
+                reducerName,
+            )
+          }
+          internalContext.sliceCaseReducersByName[reducerName] = reducer
+          return context
+        },
+        getInitialState,
+      }
+      return context
     }
 
     if (isCreatorCallback(options.reducers)) {
@@ -955,7 +962,11 @@ export function buildCreateSlice<
           reducerName,
           type: getType(name, reducerName),
         }
-        handler(reducerDetails, reducerDefinition as any, contextMethods)
+        handler(
+          reducerDetails,
+          reducerDefinition as any,
+          getContext(reducerDetails),
+        )
       }
     } else {
       for (const [reducerName, reducerDefinition] of Object.entries(
@@ -970,7 +981,11 @@ export function buildCreateSlice<
           'reducer' in reducerDefinition
             ? preparedReducerCreator
             : reducerCreator
-        handler.handle(reducerDetails, reducerDefinition as any, contextMethods)
+        handler.handle(
+          reducerDetails,
+          reducerDefinition as any,
+          getContext(reducerDetails),
+        )
       }
     }
 
@@ -993,14 +1008,14 @@ export function buildCreateSlice<
 
       const finalCaseReducers = {
         ...extraReducers,
-        ...context.sliceCaseReducersByType,
+        ...internalContext.sliceCaseReducersByType,
       }
 
       return createReducer(options.initialState, (builder) => {
         for (let key in finalCaseReducers) {
           builder.addCase(key, finalCaseReducers[key] as CaseReducer)
         }
-        for (let sM of context.sliceMatchers) {
+        for (let sM of internalContext.sliceMatchers) {
           builder.addMatcher(sM.matcher, sM.reducer)
         }
         for (let m of actionMatchers) {
@@ -1101,8 +1116,8 @@ export function buildCreateSlice<
     > = {
       name,
       reducer,
-      actions: context.actionCreators as any,
-      caseReducers: context.sliceCaseReducersByName as any,
+      actions: internalContext.actionCreators as any,
+      caseReducers: internalContext.sliceCaseReducersByName as any,
       getInitialState,
       ...makeSelectorProps(reducerPath),
       injectInto(injectable, { reducerPath: pathOpt, ...config } = {}) {
diff --git a/packages/toolkit/src/tests/createSlice.test-d.ts b/packages/toolkit/src/tests/createSlice.test-d.ts
index 94e756bc6c..aca1ea43c4 100644
--- a/packages/toolkit/src/tests/createSlice.test-d.ts
+++ b/packages/toolkit/src/tests/createSlice.test-d.ts
@@ -821,7 +821,7 @@ describe('type tests', () => {
           .addCase(toastClosed, (state, action) => {
             delete state.toasts[action.payload]
           })
-          .exposeAction(reducerName, openToast)
+          .exposeAction(openToast)
       },
     }
 
diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index f90b092056..94f9952c99 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -730,8 +730,8 @@ describe('createSlice', () => {
         if (started) context.addCase(startedAction, started)
         if (ended) context.addCase(endedAction, ended)
 
-        context.exposeAction(reducerName, thunkCreator)
-        context.exposeCaseReducer(reducerName, { started, ended })
+        context.exposeAction(thunkCreator)
+        context.exposeCaseReducer({ started, ended })
       },
     }
     test('allows passing custom reducer creators, which can add actions and case reducers', () => {

From 90555eeb22732ab6cc9fff2832f650bb4d27e71b Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Fri, 5 Apr 2024 12:36:36 +0100
Subject: [PATCH 144/178] add tests for context errors

---
 packages/toolkit/src/createSlice.ts           |  4 +-
 .../toolkit/src/tests/createSlice.test.ts     | 65 +++++++++++++++++++
 2 files changed, 67 insertions(+), 2 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 089b4d287c..50d5777350 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -922,7 +922,7 @@ export function buildCreateSlice<
         exposeAction(actionCreator) {
           if (reducerName in internalContext.actionCreators) {
             throw new Error(
-              'context.exposeAction cannot be called twice for the same reducer definition:' +
+              'context.exposeAction cannot be called twice for the same reducer definition: ' +
                 reducerName,
             )
           }
@@ -932,7 +932,7 @@ export function buildCreateSlice<
         exposeCaseReducer(reducer) {
           if (reducerName in internalContext.sliceCaseReducersByName) {
             throw new Error(
-              'context.exposeCaseReducer cannot be called twice for the same reducer definition:' +
+              'context.exposeCaseReducer cannot be called twice for the same reducer definition: ' +
                 reducerName,
             )
           }
diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index 94f9952c99..773b1d4cbd 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -12,6 +12,8 @@ import type {
   ReducerCreatorEntry,
   ReducerCreators,
   ReducerDefinition,
+  ReducerDetails,
+  ReducerHandlingContext,
   SliceActionType,
   ThunkAction,
   WithSlice,
@@ -943,6 +945,69 @@ describe('createSlice', () => {
         expect(selectValue(store.getState())).toBe(1)
       })
     })
+    describe('context methods throw errors if used incorrectly', () => {
+      const makeSliceWithHandler = (
+        handle: ReducerCreator<typeof loaderCreatorType>['handle'],
+      ) => {
+        const createAppSlice = buildCreateSlice({
+          creators: {
+            loader: {
+              type: loaderCreatorType,
+              create(reducers) {
+                return {
+                  _reducerDefinitionType: loaderCreatorType,
+                  ...reducers,
+                }
+              },
+              handle,
+            } satisfies ReducerCreator<typeof loaderCreatorType>,
+          },
+        })
+        return createAppSlice({
+          name: 'loader',
+          initialState: {} as Partial<Record<string, true>>,
+          reducers: (create) => ({
+            addLoader: create.loader({}),
+          }),
+        })
+      }
+      test('context.addCase throws if called twice for same type', () => {
+        expect(() =>
+          makeSliceWithHandler((_details, _def, context) => {
+            context.addCase('foo', () => {}).addCase('foo', () => {})
+          }),
+        ).toThrowErrorMatchingInlineSnapshot(
+          `[Error: \`context.addCase\` cannot be called with two reducers for the same action type: foo]`,
+        )
+      })
+      test('context.addCase throws if empty action type', () => {
+        expect(() =>
+          makeSliceWithHandler((_details, _def, context) => {
+            context.addCase('', () => {})
+          }),
+        ).toThrowErrorMatchingInlineSnapshot(
+          `[Error: \`context.addCase\` cannot be called with an empty action type]`,
+        )
+      })
+      test('context.exposeAction throws if called twice for same reducer name', () => {
+        expect(() =>
+          makeSliceWithHandler((_details, _def, context) => {
+            context.exposeAction(() => {}).exposeAction(() => {})
+          }),
+        ).toThrowErrorMatchingInlineSnapshot(
+          `[Error: context.exposeAction cannot be called twice for the same reducer definition: addLoader]`,
+        )
+      })
+      test('context.exposeCaseReducer throws if called twice for same reducer name', () => {
+        expect(() =>
+          makeSliceWithHandler((_details, _def, context) => {
+            context.exposeCaseReducer({}).exposeCaseReducer({})
+          }),
+        ).toThrowErrorMatchingInlineSnapshot(
+          `[Error: context.exposeCaseReducer cannot be called twice for the same reducer definition: addLoader]`,
+        )
+      })
+    })
   })
 })
 

From 2e25c1f3e720f8b678f42e5b409bfe1002aa397a Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Fri, 5 Apr 2024 12:43:11 +0100
Subject: [PATCH 145/178] add note about only calling methods once

---
 packages/toolkit/src/createSlice.ts | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 50d5777350..5793b58b0d 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -227,6 +227,9 @@ export interface ReducerHandlingContext<State> {
   ): ReducerHandlingContext<State>
   /**
    * Add an action to be exposed under the final `slice.actions[reducerName]` key.
+   *
+   * Should only be called once per handler.
+   *
    * @param actionCreator The action to expose.
    * @example
    * context.exposeAction(createAction<Post>(type));
@@ -238,6 +241,9 @@ export interface ReducerHandlingContext<State> {
   exposeAction(actionCreator: unknown): ReducerHandlingContext<State>
   /**
    * Add a case reducer to be exposed under the final `slice.caseReducers[reducerName]` key.
+   *
+   * Should only be called once per handler.
+   *
    * @param reducer The reducer to expose.
    * @example
    * context.exposeCaseReducer((state, action: PayloadAction<Post>) => {

From e3b8d90950291f011cf3dd769cbff941ad523164 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Fri, 5 Apr 2024 17:56:12 +0100
Subject: [PATCH 146/178] expose selectSlice, name and reducerPath in context

---
 packages/toolkit/src/createSlice.ts | 62 ++++++++++++++++++++++++-----
 1 file changed, 51 insertions(+), 11 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 5793b58b0d..e4e31490ed 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -190,7 +190,11 @@ interface InternalReducerHandlingContext<State> {
   actionCreators: Record<string, any>
 }
 
-export interface ReducerHandlingContext<State> {
+export interface ReducerHandlingContext<
+  State,
+  Name extends string,
+  ReducerPath extends string,
+> {
   /**
    * Adds a case reducer to handle a single action type.
    * @param actionCreator - Either a plain action type string, or an action creator generated by [`createAction`](./createAction) that can be used to determine the action type.
@@ -199,7 +203,7 @@ export interface ReducerHandlingContext<State> {
   addCase<ActionCreator extends TypedActionCreator<string>>(
     actionCreator: ActionCreator,
     reducer: CaseReducer<State, ReturnType<ActionCreator>>,
-  ): ReducerHandlingContext<State>
+  ): ReducerHandlingContext<State, Name, ReducerPath>
   /**
    * Adds a case reducer to handle a single action type.
    * @param actionCreator - Either a plain action type string, or an action creator generated by [`createAction`](./createAction) that can be used to determine the action type.
@@ -208,7 +212,7 @@ export interface ReducerHandlingContext<State> {
   addCase<Type extends string, A extends Action<Type>>(
     type: Type,
     reducer: CaseReducer<State, A>,
-  ): ReducerHandlingContext<State>
+  ): ReducerHandlingContext<State, Name, ReducerPath>
 
   /**
    * Allows you to match incoming actions against your own filter function instead of only the `action.type` property.
@@ -224,7 +228,7 @@ export interface ReducerHandlingContext<State> {
   addMatcher<A>(
     matcher: TypeGuard<A>,
     reducer: CaseReducer<State, A extends Action ? A : A & Action>,
-  ): ReducerHandlingContext<State>
+  ): ReducerHandlingContext<State, Name, ReducerPath>
   /**
    * Add an action to be exposed under the final `slice.actions[reducerName]` key.
    *
@@ -238,7 +242,10 @@ export interface ReducerHandlingContext<State> {
    *
    * dispatch(addPost(post))
    */
-  exposeAction(actionCreator: unknown): ReducerHandlingContext<State>
+  exposeAction(
+    actionCreator: unknown,
+  ): ReducerHandlingContext<State, Name, ReducerPath>
+
   /**
    * Add a case reducer to be exposed under the final `slice.caseReducers[reducerName]` key.
    *
@@ -252,12 +259,34 @@ export interface ReducerHandlingContext<State> {
    *
    * slice.caseReducers.addPost([], addPost(post))
    */
-  exposeCaseReducer(reducer: unknown): ReducerHandlingContext<State>
+  exposeCaseReducer(
+    reducer: unknown,
+  ): ReducerHandlingContext<State, Name, ReducerPath>
+
   /**
    * Provides access to the initial state value given to the slice.
    * If a lazy state initializer was provided, it will be called and a fresh value returned.
    */
   getInitialState(): State
+
+  /**
+   * The `name` option provided for the slice.
+   */
+  name: Name
+
+  /**
+   * The `reducerPath` option provided for the slice.
+   * Defaults to `name` if not provided.
+   */
+  reducerPath: ReducerPath
+
+  /**
+   * Tries to select the slice's state from a possible root state shape, using `reducerPath`.
+   * Throws an error if slice's state is not found.
+   *
+   * *Note that only the original `reducerPath` option is used - if a different `reducerPath` is used when injecting, this will not be reflected.*
+   */
+  selectSlice(state: Record<ReducerPath, State>): State
 }
 
 export interface ReducerDetails {
@@ -314,10 +343,10 @@ export type ReducerCreator<Type extends RegisteredReducerType> = {
 } & (ReducerDefinitionsForType<Type> extends never
   ? {}
   : {
-      handle<State>(
+      handle<State, Name extends string, ReducerPath extends string>(
         details: ReducerDetails,
         definition: ReducerDefinitionsForType<Type>,
-        context: ReducerHandlingContext<State>,
+        context: ReducerHandlingContext<State, Name, ReducerPath>,
       ): void
     })
 
@@ -388,7 +417,7 @@ export interface Slice<
    * Equivalent to `slice.getSelectors((state: RootState) => state[slice.reducerPath])`.
    */
   get selectors(): Id<
-    SliceDefinedSelectors<State, Selectors, { [K in ReducerPath]: State }>
+    SliceDefinedSelectors<State, Selectors, Record<ReducerPath, State>>
   >
 
   /**
@@ -410,7 +439,7 @@ export interface Slice<
    *
    * Will throw an error if slice is not found.
    */
-  selectSlice(state: { [K in ReducerPath]: State }): State
+  selectSlice(state: Record<ReducerPath, State>): State
 }
 
 /**
@@ -898,7 +927,7 @@ export function buildCreateSlice<
     }
 
     function getContext({ reducerName }: ReducerDetails) {
-      const context: ReducerHandlingContext<State> = {
+      const context: ReducerHandlingContext<State, Name, ReducerPath> = {
         addCase(
           typeOrActionCreator: string | TypedActionCreator<any>,
           reducer: CaseReducer<State>,
@@ -946,6 +975,17 @@ export function buildCreateSlice<
           return context
         },
         getInitialState,
+        name,
+        reducerPath,
+        selectSlice(state) {
+          const sliceState = state[reducerPath]
+          if (typeof sliceState === 'undefined') {
+            throw new Error(
+              `Could not find "${name}" slice in state. In order for slice creators to use \`context.selectSlice\`, the slice must be nested in the state under its reducerPath: "${reducerPath}"`,
+            )
+          }
+          return sliceState
+        },
       }
       return context
     }

From 2cb7e479bef50ecf1bb1a59975d8013fdb1b76cd Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Fri, 5 Apr 2024 18:37:23 +0100
Subject: [PATCH 147/178] expose Name and ReducerPath in types, and add test
 for selectSlice

---
 packages/toolkit/src/createSlice.ts           | 103 +++++++++-----
 .../toolkit/src/tests/createSlice.test-d.ts   |   3 +-
 .../toolkit/src/tests/createSlice.test.ts     | 132 +++++++++++++++++-
 3 files changed, 201 insertions(+), 37 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index e4e31490ed..04131d5b63 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -32,7 +32,12 @@ export enum ReducerType {
   asyncThunk = 'asyncThunk',
 }
 
-export type RegisteredReducerType = keyof SliceReducerCreators<any, any, any>
+export type RegisteredReducerType = keyof SliceReducerCreators<
+  any,
+  any,
+  any,
+  any
+>
 
 export interface ReducerDefinition<
   T extends RegisteredReducerType = RegisteredReducerType,
@@ -64,6 +69,7 @@ export interface SliceReducerCreators<
   State,
   CaseReducers extends CreatorCaseReducers<State>,
   Name extends string,
+  ReducerPath extends string,
 > {
   [ReducerType.reducer]: ReducerCreatorEntry<
     {
@@ -164,22 +170,36 @@ export interface SliceReducerCreators<
 
 export type ReducerCreators<
   State,
+  Name extends string = string,
+  ReducerPath extends string = Name,
   CreatorMap extends Record<string, RegisteredReducerType> = {},
 > = {
-  reducer: SliceReducerCreators<State, any, any>[ReducerType.reducer]['create']
+  reducer: SliceReducerCreators<
+    State,
+    any,
+    Name,
+    ReducerPath
+  >[ReducerType.reducer]['create']
   preparedReducer: SliceReducerCreators<
     State,
     any,
-    any
+    Name,
+    ReducerPath
   >[ReducerType.reducerWithPrepare]['create']
 } & {
-  [Name in keyof CreatorMap as SliceReducerCreators<
+  [CreatorName in keyof CreatorMap as SliceReducerCreators<
     State,
     any,
-    any
-  >[CreatorMap[Name]]['create'] extends never
+    Name,
+    ReducerPath
+  >[CreatorMap[CreatorName]]['create'] extends never
     ? never
-    : Name]: SliceReducerCreators<State, any, any>[CreatorMap[Name]]['create']
+    : CreatorName]: SliceReducerCreators<
+    State,
+    any,
+    Name,
+    ReducerPath
+  >[CreatorMap[CreatorName]]['create']
 }
 
 interface InternalReducerHandlingContext<State> {
@@ -192,8 +212,8 @@ interface InternalReducerHandlingContext<State> {
 
 export interface ReducerHandlingContext<
   State,
-  Name extends string,
-  ReducerPath extends string,
+  Name extends string = string,
+  ReducerPath extends string = Name,
 > {
   /**
    * Adds a case reducer to handle a single action type.
@@ -317,15 +337,19 @@ type RecursiveExtractDefinition<
 type ReducerDefinitionsForType<Type extends RegisteredReducerType> = {
   [CreatorType in RegisteredReducerType]:
     | RecursiveExtractDefinition<
-        ReturnType<SliceReducerCreators<any, any, any>[CreatorType]['create']>,
+        ReturnType<
+          SliceReducerCreators<any, any, any, any>[CreatorType]['create']
+        >,
         Type
       >
     | {
         [K in keyof SliceReducerCreators<
+          any,
           any,
           any,
           any
         >[CreatorType]['create']]: SliceReducerCreators<
+          any,
           any,
           any,
           any
@@ -334,12 +358,12 @@ type ReducerDefinitionsForType<Type extends RegisteredReducerType> = {
         ) => infer Definitions
           ? RecursiveExtractDefinition<Definitions, Type>
           : never
-      }[keyof SliceReducerCreators<any, any, any>[CreatorType]['create']]
-}[keyof SliceReducerCreators<any, any, any>]
+      }[keyof SliceReducerCreators<any, any, any, any>[CreatorType]['create']]
+}[RegisteredReducerType]
 
 export type ReducerCreator<Type extends RegisteredReducerType> = {
   type: Type
-  create: SliceReducerCreators<any, any, any>[Type]['create']
+  create: SliceReducerCreators<any, any, any, any>[Type]['create']
 } & (ReducerDefinitionsForType<Type> extends never
   ? {}
   : {
@@ -385,13 +409,13 @@ export interface Slice<
    * Action creators for the types of actions that are handled by the slice
    * reducer.
    */
-  actions: CaseReducerActions<CaseReducers, Name, State>
+  actions: CaseReducerActions<CaseReducers, Name, ReducerPath, State>
 
   /**
    * The individual case reducer functions that were passed in the `reducers` parameter.
    * This enables reuse and testing if they were defined inline when calling `createSlice`.
    */
-  caseReducers: SliceDefinedCaseReducers<CaseReducers, Name, State>
+  caseReducers: SliceDefinedCaseReducers<CaseReducers, Name, ReducerPath, State>
 
   /**
    * Provides access to the initial state value given to the slice.
@@ -492,16 +516,25 @@ interface InjectedSlice<
 
 type CreatorCallback<
   State,
+  Name extends string,
+  ReducerPath extends string,
   CreatorMap extends Record<string, RegisteredReducerType>,
 > = (
-  create: ReducerCreators<State, CreatorMap>,
+  create: ReducerCreators<State, Name, ReducerPath, CreatorMap>,
 ) => Record<string, ReducerDefinition>
 
 type GetCaseReducers<
   State,
+  Name extends string,
+  ReducerPath extends string,
   CreatorMap extends Record<string, RegisteredReducerType>,
-  CR extends SliceCaseReducers<State> | CreatorCallback<State, CreatorMap>,
-> = CR extends CreatorCallback<State, CreatorMap> ? ReturnType<CR> : CR
+  CR extends
+    | SliceCaseReducers<State>
+    | CreatorCallback<State, Name, ReducerPath, CreatorMap>,
+> =
+  CR extends CreatorCallback<State, Name, ReducerPath, CreatorMap>
+    ? ReturnType<CR>
+    : CR
 
 /**
  * Options for `createSlice()`.
@@ -512,7 +545,12 @@ export interface CreateSliceOptions<
   State = any,
   CR extends
     | SliceCaseReducers<State>
-    | CreatorCallback<State, CreatorMap> = SliceCaseReducers<State>,
+    | CreatorCallback<
+        State,
+        Name,
+        ReducerPath,
+        CreatorMap
+      > = SliceCaseReducers<State>,
   Name extends string = string,
   ReducerPath extends string = Name,
   Selectors extends SliceSelectors<State> = SliceSelectors<State>,
@@ -658,6 +696,7 @@ type ConvertNeverKeysToUnknown<T> = T extends any
 export type CaseReducerActions<
   CaseReducers extends CreatorCaseReducers<State>,
   SliceName extends string,
+  ReducerPath extends string = SliceName,
   State = any,
 > = Id<
   UnionToIntersection<
@@ -665,7 +704,8 @@ export type CaseReducerActions<
       SliceReducerCreators<
         State,
         CaseReducers,
-        SliceName
+        SliceName,
+        ReducerPath
       >[RegisteredReducerType]['actions']
     >
   >
@@ -704,6 +744,7 @@ type ActionCreatorForCaseReducer<CR, Type extends string> = CR extends (
 type SliceDefinedCaseReducers<
   CaseReducers extends CreatorCaseReducers<State>,
   SliceName extends string = string,
+  ReducerPath extends string = SliceName,
   State = any,
 > = Id<
   UnionToIntersection<
@@ -711,7 +752,8 @@ type SliceDefinedCaseReducers<
       SliceReducerCreators<
         State,
         CaseReducers,
-        SliceName
+        SliceName,
+        ReducerPath
       >[RegisteredReducerType]['caseReducers']
     >
   >
@@ -752,7 +794,7 @@ type SliceDefinedSelectors<
  */
 export type ValidateSliceCaseReducers<
   S,
-  ACR extends SliceCaseReducers<S> | CreatorCallback<S, any>,
+  ACR extends SliceCaseReducers<S> | CreatorCallback<S, any, any, any>,
 > = ACR & {
   [T in keyof ACR]: ACR[T] extends {
     reducer(s: S, action?: infer A): any
@@ -809,12 +851,9 @@ export const preparedReducerCreator: ReducerCreator<ReducerType.reducerWithPrepa
     },
   }
 
-const isCreatorCallback = <
-  State,
-  CreatorMap extends Record<string, RegisteredReducerType>,
->(
-  reducers: any,
-): reducers is CreatorCallback<State, CreatorMap> =>
+const isCreatorCallback = (
+  reducers: unknown,
+): reducers is CreatorCallback<any, any, any, any> =>
   typeof reducers === 'function'
 
 interface BuildCreateSliceConfig<
@@ -881,7 +920,7 @@ export function buildCreateSlice<
     State,
     CaseReducers extends
       | SliceCaseReducers<State>
-      | CreatorCallback<State, CreatorMap>,
+      | CreatorCallback<State, Name, ReducerPath, CreatorMap>,
     Name extends string,
     Selectors extends SliceSelectors<State>,
     ReducerPath extends string = Name,
@@ -896,7 +935,7 @@ export function buildCreateSlice<
     >,
   ): Slice<
     State,
-    GetCaseReducers<State, CreatorMap, CaseReducers>,
+    GetCaseReducers<State, Name, ReducerPath, CreatorMap, CaseReducers>,
     Name,
     ReducerPath,
     Selectors
@@ -1097,7 +1136,7 @@ export function buildCreateSlice<
     ): Pick<
       Slice<
         State,
-        GetCaseReducers<State, CreatorMap, CaseReducers>,
+        GetCaseReducers<State, Name, ReducerPath, CreatorMap, CaseReducers>,
         Name,
         CurrentReducerPath,
         Selectors
@@ -1153,7 +1192,7 @@ export function buildCreateSlice<
 
     const slice: Slice<
       State,
-      CaseReducers extends CreatorCallback<State, CreatorMap>
+      CaseReducers extends CreatorCallback<State, Name, ReducerPath, CreatorMap>
         ? ReturnType<CaseReducers>
         : CaseReducers,
       Name,
diff --git a/packages/toolkit/src/tests/createSlice.test-d.ts b/packages/toolkit/src/tests/createSlice.test-d.ts
index aca1ea43c4..ea0308ce31 100644
--- a/packages/toolkit/src/tests/createSlice.test-d.ts
+++ b/packages/toolkit/src/tests/createSlice.test-d.ts
@@ -796,7 +796,7 @@ describe('type tests', () => {
       create: () => ({
         _reducerDefinitionType: toasterCreatorType,
       }),
-      handle({ type, reducerName }, _definition, context) {
+      handle({ type }, _definition, context) {
         const toastOpened = createAction<{ message: string; id: string }>(
           type + '/opened',
         )
@@ -886,6 +886,7 @@ declare module '@reduxjs/toolkit' {
     State,
     CaseReducers extends CreatorCaseReducers<State>,
     Name extends string,
+    ReducerPath extends string,
   > {
     [toasterCreatorType]: ReducerCreatorEntry<
       State extends ToastState
diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index 773b1d4cbd..48c995f21a 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -1,5 +1,5 @@
 import { vi } from 'vitest'
-import type { Patch } from 'immer'
+import type { Draft, Patch } from 'immer'
 import { applyPatches, enablePatches, produceWithPatches } from 'immer'
 import type {
   Action,
@@ -40,9 +40,10 @@ enablePatches()
 
 type CreateSlice = typeof createSlice
 
-const loaderCreatorType = Symbol()
-const historyMethodsCreatorType = Symbol()
-const undoableCreatorType = Symbol()
+const loaderCreatorType = Symbol('loaderCreatorType')
+const historyMethodsCreatorType = Symbol('historyMethodsCreatorType')
+const undoableCreatorType = Symbol('undoableCreatorType')
+const patchCreatorType = Symbol('patchCreatorType')
 
 describe('createSlice', () => {
   let restore: () => void
@@ -1007,6 +1008,86 @@ describe('createSlice', () => {
           `[Error: context.exposeCaseReducer cannot be called twice for the same reducer definition: addLoader]`,
         )
       })
+      test('context.selectSlice throws if unable to find slice state', () => {
+        const patchCreator: ReducerCreator<typeof patchCreatorType> = {
+          type: patchCreatorType,
+          create() {
+            return { _reducerDefinitionType: patchCreatorType }
+          },
+          handle({ type }, _def, context) {
+            const patchedAction = createAction<Patch[]>(type)
+            function patchThunk(
+              recipe: (draft: Draft<any>) => void,
+            ): ThunkAction<void, Record<string, any>, unknown, Action> {
+              return (dispatch, getState) => {
+                const [, patches] = produceWithPatches(
+                  context.selectSlice(getState()),
+                  recipe,
+                )
+                dispatch(patchedAction(patches))
+              }
+            }
+            Object.assign(patchThunk, { patched: patchedAction })
+
+            function applyPatchesReducer(
+              state: Objectish,
+              action: PayloadAction<Patch[]>,
+            ) {
+              return applyPatches(state, action.payload)
+            }
+
+            ;(context as ReducerHandlingContext<Objectish>)
+              .addCase(patchedAction, applyPatchesReducer)
+              .exposeAction(patchThunk)
+              .exposeCaseReducer(applyPatchesReducer)
+          },
+        }
+
+        const createAppSlice = buildCreateSlice({
+          creators: { patcher: patchCreator },
+        })
+
+        const personSlice = createAppSlice({
+          name: 'person',
+          initialState: { name: 'Alice' },
+          reducers: (create) => ({
+            patchPerson: create.patcher(),
+          }),
+        })
+
+        const { patchPerson } = personSlice.actions
+
+        const correctStore = configureStore({
+          reducer: combineSlices(personSlice),
+        })
+
+        expect(correctStore.getState().person.name).toBe('Alice')
+
+        expect(() =>
+          correctStore.dispatch(
+            patchPerson((person) => {
+              person.name = 'Bob'
+            }),
+          ),
+        ).not.toThrow()
+
+        expect(correctStore.getState().person.name).toBe('Bob')
+
+        const incorrectStore = configureStore({
+          reducer: {
+            somewhere: personSlice.reducer,
+          },
+        })
+
+        expect(() =>
+          incorrectStore.dispatch(
+            // @ts-expect-error state mismatch
+            patchPerson((person) => {
+              person.name = 'Charlie'
+            }),
+          ),
+        ).toThrowErrorMatchingInlineSnapshot(`[Error: Could not find "person" slice in state. In order for slice creators to use \`context.selectSlice\`, the slice must be nested in the state under its reducerPath: "person"]`)
+      })
     })
   })
 })
@@ -1049,11 +1130,33 @@ interface UndoableOptions {
   undoable?: boolean
 }
 
+// nicked from immer
+type Objectish = AnyObject | AnyArray | AnyMap | AnySet
+type AnyObject = {
+  [key: string]: any
+}
+type AnyArray = Array<any>
+type AnySet = Set<any>
+type AnyMap = Map<any, any>
+
+type PatchThunk<
+  Name extends string,
+  ReducerName extends PropertyKey,
+  ReducerPath extends string,
+  State,
+> = {
+  (
+    recipe: (draft: Draft<State>) => void,
+  ): ThunkAction<void, Record<ReducerPath, State>, unknown, Action>
+  patched: PayloadActionCreator<Patch[], SliceActionType<Name, ReducerName>>
+}
+
 declare module '@reduxjs/toolkit' {
   export interface SliceReducerCreators<
     State,
     CaseReducers extends CreatorCaseReducers<State>,
     Name extends string,
+    ReducerPath extends string,
   > {
     [loaderCreatorType]: ReducerCreatorEntry<
       (
@@ -1126,5 +1229,26 @@ declare module '@reduxjs/toolkit' {
           }
         : never
     >
+    [patchCreatorType]: ReducerCreatorEntry<
+      State extends Objectish
+        ? () => ReducerDefinition<typeof patchCreatorType>
+        : never,
+      {
+        actions: {
+          [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends ReducerDefinition<
+            typeof patchCreatorType
+          >
+            ? PatchThunk<Name, ReducerName, ReducerPath, State>
+            : never
+        }
+        caseReducers: {
+          [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends ReducerDefinition<
+            typeof patchCreatorType
+          >
+            ? CaseReducer<State, PayloadAction<Patch[]>>
+            : never
+        }
+      }
+    >
   }
 }

From bfc9bae02db97d31f4679bcd45f80d067814c968 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Fri, 5 Apr 2024 20:20:25 +0100
Subject: [PATCH 148/178] move reducerPath to reducerDetails

---
 packages/toolkit/src/createSlice.ts | 45 +++++++++--------------------
 1 file changed, 14 insertions(+), 31 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 04131d5b63..33710089bd 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -210,11 +210,7 @@ interface InternalReducerHandlingContext<State> {
   actionCreators: Record<string, any>
 }
 
-export interface ReducerHandlingContext<
-  State,
-  Name extends string = string,
-  ReducerPath extends string = Name,
-> {
+export interface ReducerHandlingContext<State> {
   /**
    * Adds a case reducer to handle a single action type.
    * @param actionCreator - Either a plain action type string, or an action creator generated by [`createAction`](./createAction) that can be used to determine the action type.
@@ -223,7 +219,7 @@ export interface ReducerHandlingContext<
   addCase<ActionCreator extends TypedActionCreator<string>>(
     actionCreator: ActionCreator,
     reducer: CaseReducer<State, ReturnType<ActionCreator>>,
-  ): ReducerHandlingContext<State, Name, ReducerPath>
+  ): ReducerHandlingContext<State>
   /**
    * Adds a case reducer to handle a single action type.
    * @param actionCreator - Either a plain action type string, or an action creator generated by [`createAction`](./createAction) that can be used to determine the action type.
@@ -232,7 +228,7 @@ export interface ReducerHandlingContext<
   addCase<Type extends string, A extends Action<Type>>(
     type: Type,
     reducer: CaseReducer<State, A>,
-  ): ReducerHandlingContext<State, Name, ReducerPath>
+  ): ReducerHandlingContext<State>
 
   /**
    * Allows you to match incoming actions against your own filter function instead of only the `action.type` property.
@@ -248,7 +244,7 @@ export interface ReducerHandlingContext<
   addMatcher<A>(
     matcher: TypeGuard<A>,
     reducer: CaseReducer<State, A extends Action ? A : A & Action>,
-  ): ReducerHandlingContext<State, Name, ReducerPath>
+  ): ReducerHandlingContext<State>
   /**
    * Add an action to be exposed under the final `slice.actions[reducerName]` key.
    *
@@ -262,9 +258,7 @@ export interface ReducerHandlingContext<
    *
    * dispatch(addPost(post))
    */
-  exposeAction(
-    actionCreator: unknown,
-  ): ReducerHandlingContext<State, Name, ReducerPath>
+  exposeAction(actionCreator: unknown): ReducerHandlingContext<State>
 
   /**
    * Add a case reducer to be exposed under the final `slice.caseReducers[reducerName]` key.
@@ -279,9 +273,7 @@ export interface ReducerHandlingContext<
    *
    * slice.caseReducers.addPost([], addPost(post))
    */
-  exposeCaseReducer(
-    reducer: unknown,
-  ): ReducerHandlingContext<State, Name, ReducerPath>
+  exposeCaseReducer(reducer: unknown): ReducerHandlingContext<State>
 
   /**
    * Provides access to the initial state value given to the slice.
@@ -289,29 +281,20 @@ export interface ReducerHandlingContext<
    */
   getInitialState(): State
 
-  /**
-   * The `name` option provided for the slice.
-   */
-  name: Name
-
-  /**
-   * The `reducerPath` option provided for the slice.
-   * Defaults to `name` if not provided.
-   */
-  reducerPath: ReducerPath
-
   /**
    * Tries to select the slice's state from a possible root state shape, using `reducerPath`.
    * Throws an error if slice's state is not found.
    *
    * *Note that only the original `reducerPath` option is used - if a different `reducerPath` is used when injecting, this will not be reflected.*
    */
-  selectSlice(state: Record<ReducerPath, State>): State
+  selectSlice(state: Record<string, State>): State
 }
 
 export interface ReducerDetails {
   /** The name of the slice */
   sliceName: string
+  /** The reducerPath option passed for the slice. Defaults to `sliceName` if not provided. */
+  reducerPath: string
   /** The key the reducer was defined under */
   reducerName: string
   /** The predefined action type, i.e. `${sliceName}/${reducerName}` */
@@ -367,10 +350,10 @@ export type ReducerCreator<Type extends RegisteredReducerType> = {
 } & (ReducerDefinitionsForType<Type> extends never
   ? {}
   : {
-      handle<State, Name extends string, ReducerPath extends string>(
+      handle<State>(
         details: ReducerDetails,
         definition: ReducerDefinitionsForType<Type>,
-        context: ReducerHandlingContext<State, Name, ReducerPath>,
+        context: ReducerHandlingContext<State>,
       ): void
     })
 
@@ -966,7 +949,7 @@ export function buildCreateSlice<
     }
 
     function getContext({ reducerName }: ReducerDetails) {
-      const context: ReducerHandlingContext<State, Name, ReducerPath> = {
+      const context: ReducerHandlingContext<State> = {
         addCase(
           typeOrActionCreator: string | TypedActionCreator<any>,
           reducer: CaseReducer<State>,
@@ -1014,8 +997,6 @@ export function buildCreateSlice<
           return context
         },
         getInitialState,
-        name,
-        reducerPath,
         selectSlice(state) {
           const sliceState = state[reducerPath]
           if (typeof sliceState === 'undefined') {
@@ -1045,6 +1026,7 @@ export function buildCreateSlice<
         const reducerDetails: ReducerDetails = {
           sliceName: name,
           reducerName,
+          reducerPath,
           type: getType(name, reducerName),
         }
         handler(
@@ -1060,6 +1042,7 @@ export function buildCreateSlice<
         const reducerDetails: ReducerDetails = {
           sliceName: name,
           reducerName,
+          reducerPath,
           type: getType(name, reducerName),
         }
         const handler =

From 51aecbcf7bc78c1cd505ddb05f84e9a5ae4bd35b Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Fri, 5 Apr 2024 20:23:48 +0100
Subject: [PATCH 149/178] simplify selectSlice types

---
 packages/toolkit/src/createSlice.ts | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 33710089bd..087784c72e 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -287,7 +287,7 @@ export interface ReducerHandlingContext<State> {
    *
    * *Note that only the original `reducerPath` option is used - if a different `reducerPath` is used when injecting, this will not be reflected.*
    */
-  selectSlice(state: Record<string, State>): State
+  selectSlice(state: Record<string, unknown>): State
 }
 
 export interface ReducerDetails {
@@ -1004,7 +1004,7 @@ export function buildCreateSlice<
               `Could not find "${name}" slice in state. In order for slice creators to use \`context.selectSlice\`, the slice must be nested in the state under its reducerPath: "${reducerPath}"`,
             )
           }
-          return sliceState
+          return sliceState as State
         },
       }
       return context

From 27950e133bd803b83ccd38df2e90cea0f69bb0d9 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Fri, 5 Apr 2024 20:27:33 +0100
Subject: [PATCH 150/178] update docs definitions to match

---
 docs/usage/custom-slice-creators.mdx | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/docs/usage/custom-slice-creators.mdx b/docs/usage/custom-slice-creators.mdx
index 1241ac172d..5db026796e 100644
--- a/docs/usage/custom-slice-creators.mdx
+++ b/docs/usage/custom-slice-creators.mdx
@@ -498,6 +498,7 @@ declare module '@reduxjs/toolkit' {
     State,
     CaseReducers extends CreatorCaseReducers<State>,
     Name extends string,
+    ReducerPath extends string,
   > {
     [reducerCreatorType]: ReducerCreatorEntry<
       () => ReducerDefinition<typeof reducerCreatorType>
@@ -538,6 +539,7 @@ declare module '@reduxjs/toolkit' {
     State,
     CaseReducers extends CreatorCaseReducers<State>,
     Name extends string,
+    ReducerPath extends string,
   > {
     [batchedCreatorType]: ReducerCreatorEntry<
       <Payload>(
@@ -576,6 +578,7 @@ declare module '@reduxjs/toolkit' {
     State,
     CaseReducers extends CreatorCaseReducers<State>,
     Name extends string,
+    ReducerPath extends string,
   > {
     [loaderCreatorType]: ReducerCreatorEntry<
       // highlight-next-line
@@ -602,6 +605,7 @@ declare module '@reduxjs/toolkit' {
     State,
     CaseReducers extends CreatorCaseReducers<State>,
     Name extends string,
+    ReducerPath extends string,
   > {
     [loaderCreatorType]: ReducerCreatorEntry<
       () => {
@@ -643,6 +647,7 @@ declare module '@reduxjs/toolkit' {
     State,
     CaseReducers extends CreatorCaseReducers<State>,
     Name extends string,
+    ReducerPath extends string,
   > {
     [asyncThunkCreatorType]: ReducerCreatorEntry<
       <ThunkArg, Returned>(
@@ -680,6 +685,7 @@ declare module '@reduxjs/toolkit' {
     State,
     CaseReducers extends CreatorCaseReducers<State>,
     Name extends string,
+    ReducerPath extends string,
   > {
     [preparedReducerType]: ReducerCreatorEntry<
       <Prepare extends PrepareAction<any>>(
@@ -790,6 +796,7 @@ declare module '@reduxjs/toolkit' {
     State,
     CaseReducers extends CreatorCaseReducers<State>,
     Name extends string,
+    ReducerPath extends string,
   > {
     [toastCreatorType]: ReducerCreatorEntry<
       (config: ToastReducerConfig<State>) => ToastReducerDefinition<State>,
@@ -914,6 +921,7 @@ declare module '@reduxjs/toolkit' {
     State,
     CaseReducers extends CreatorCaseReducers<State>,
     Name extends string,
+    ReducerPath extends string,
   > {
     [paginationCreatorType]: ReducerCreatorEntry<
       // make sure the creator is only called when state is compatible
@@ -984,6 +992,7 @@ declare module '@reduxjs/toolkit' {
     State,
     CaseReducers extends CreatorCaseReducers<State>,
     Name extends string,
+    ReducerPath extends string,
   > {
     [historyCreatorType]: ReducerCreatorEntry<
       // make sure the creator is only called when state is compatibleState extends HistoryState<unknown>
@@ -1097,6 +1106,7 @@ declare module '@reduxjs/toolkit' {
     State,
     CaseReducers extends CreatorCaseReducers<State>,
     Name extends string,
+    ReducerPath extends string,
   > {
     [undoableCreatorType]: ReducerCreatorEntry<
       State extends HistoryState<infer Data>

From 8c1a64fc0e17bc4df0dddb5b66988f08d069f534 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Fri, 5 Apr 2024 20:28:34 +0100
Subject: [PATCH 151/178] update errors.json

---
 errors.json | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/errors.json b/errors.json
index b11c1b5461..52d91f96a4 100644
--- a/errors.json
+++ b/errors.json
@@ -43,5 +43,8 @@
   "41": "Cannot use reserved creator type: ",
   "42": "Unsupported reducer type: ",
   "43": "If provided, `asyncThunk` creator must be `asyncThunkCreator` from '@reduxjs/toolkit'",
-  "44": "called \\`injectEndpoints\\` to override already-existing endpointName  without specifying \\`overrideExisting: true\\`"
+  "44": "called \\`injectEndpoints\\` to override already-existing endpointName  without specifying \\`overrideExisting: true\\`",
+  "45": "context.exposeAction cannot be called twice for the same reducer definition: reducerName",
+  "46": "context.exposeCaseReducer cannot be called twice for the same reducer definition: reducerName",
+  "47": "Could not find \"\" slice in state. In order for slice creators to use \\`context.selectSlice\\`, the slice must be nested in the state under its reducerPath: \"\""
 }
\ No newline at end of file

From 3f82d47287ec661c6e7e5bc933cb024db461ff32 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Fri, 5 Apr 2024 20:43:28 +0100
Subject: [PATCH 152/178] update docs with new options

---
 docs/usage/custom-slice-creators.mdx | 20 ++++++++++++++++++--
 1 file changed, 18 insertions(+), 2 deletions(-)

diff --git a/docs/usage/custom-slice-creators.mdx b/docs/usage/custom-slice-creators.mdx
index 5db026796e..1c61b32e72 100644
--- a/docs/usage/custom-slice-creators.mdx
+++ b/docs/usage/custom-slice-creators.mdx
@@ -429,10 +429,12 @@ A creator only needs a `handle` callback if it expects to be called with reducer
 
 It receives three arguments: details about the reducer, the definition, and a `context` object with methods to modify the slice.
 
-The reducer details object has two properties:
+The reducer details object has the following properties:
 
+- `sliceName` - the name of the slice the reducer is being added to (e.g. `entities/todos`)
+- `reducerPath` - the `reducerPath` passed to `createSlice` (e.g. `todos`) (defaults to `sliceName` if not provided)
 - `reducerName` - the key the reducer definition was under (e.g. `addTodo`)
-- `type` - the automatically generated type string for the reducer (e.g. `todos/addTodo`)
+- `type` - the automatically generated type string for the reducer (e.g. `entities/todos/addTodo`)
 
 The context object includes:
 
@@ -484,6 +486,19 @@ context
   .exposeCaseReducer(resetReducer)
 ```
 
+#### `selectSlice`
+
+Tries to select the slice's state from the root state, using the original `reducerPath` option passed when calling `createSlice` (which defaults to the `name` option). Throws an error if it can't find the slice.
+
+```ts
+const aThunk =
+  (): ThunkAction<void, Record<string, unknown>, unknown, Action> =>
+  (dispatch, getState) => {
+    const state = context.selectSlice(getState())
+    // do something with state
+  }
+```
+
 ### Typescript
 
 The Typescript system for custom slice creators uses a "creator registry" system similar to the module system for [RTK Query](/rtk-query/usage/customizing-create-api#creating-your-own-module).
@@ -512,6 +527,7 @@ The type parameters for `SliceReducerCreators` are:
 - `State` - The state type used by the slice.
 - `CaseReducers` - The case reducer definitions returned by the creator callback.
 - `Name` - The [`name`](../api/createSlice#name) used by the slice.
+- `ReducerPath` - The [`reducerPath`](../api/createSlice#reducerpath) used by the slice.
 
 The `ReducerCreatorEntry<Create, Exposes>` utility has two type parameters:
 

From bde0288ad4ef2b5fe8784e62a578dfda84c89e20 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Fri, 5 Apr 2024 21:00:14 +0100
Subject: [PATCH 153/178] avoid satisfies for type test

---
 .../toolkit/src/tests/createSlice.test.ts     | 25 +++++++++++--------
 1 file changed, 14 insertions(+), 11 deletions(-)

diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index 48c995f21a..b8d6333c2e 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -950,18 +950,19 @@ describe('createSlice', () => {
       const makeSliceWithHandler = (
         handle: ReducerCreator<typeof loaderCreatorType>['handle'],
       ) => {
+        const loaderCreator: ReducerCreator<typeof loaderCreatorType> = {
+          type: loaderCreatorType,
+          create(reducers) {
+            return {
+              _reducerDefinitionType: loaderCreatorType,
+              ...reducers,
+            }
+          },
+          handle,
+        }
         const createAppSlice = buildCreateSlice({
           creators: {
-            loader: {
-              type: loaderCreatorType,
-              create(reducers) {
-                return {
-                  _reducerDefinitionType: loaderCreatorType,
-                  ...reducers,
-                }
-              },
-              handle,
-            } satisfies ReducerCreator<typeof loaderCreatorType>,
+            loader: loaderCreator,
           },
         })
         return createAppSlice({
@@ -1086,7 +1087,9 @@ describe('createSlice', () => {
               person.name = 'Charlie'
             }),
           ),
-        ).toThrowErrorMatchingInlineSnapshot(`[Error: Could not find "person" slice in state. In order for slice creators to use \`context.selectSlice\`, the slice must be nested in the state under its reducerPath: "person"]`)
+        ).toThrowErrorMatchingInlineSnapshot(
+          `[Error: Could not find "person" slice in state. In order for slice creators to use \`context.selectSlice\`, the slice must be nested in the state under its reducerPath: "person"]`,
+        )
       })
     })
   })

From 1f1ac688c13cdfb83b9938687fbaf605913a15a9 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Fri, 5 Apr 2024 21:07:32 +0100
Subject: [PATCH 154/178] add no-transpile to snippet

---
 docs/usage/custom-slice-creators.mdx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/usage/custom-slice-creators.mdx b/docs/usage/custom-slice-creators.mdx
index 1c61b32e72..530d02ec88 100644
--- a/docs/usage/custom-slice-creators.mdx
+++ b/docs/usage/custom-slice-creators.mdx
@@ -490,7 +490,7 @@ context
 
 Tries to select the slice's state from the root state, using the original `reducerPath` option passed when calling `createSlice` (which defaults to the `name` option). Throws an error if it can't find the slice.
 
-```ts
+```ts no-transpile
 const aThunk =
   (): ThunkAction<void, Record<string, unknown>, unknown, Action> =>
   (dispatch, getState) => {

From 7aa39334bed9b33382c8b90293fa60e25332fce4 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sat, 6 Apr 2024 20:13:53 +0100
Subject: [PATCH 155/178] test symbol errors

---
 packages/toolkit/src/tests/createSlice.test.ts | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index b8d6333c2e..86c7d5c14b 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -738,6 +738,15 @@ describe('createSlice', () => {
       },
     }
     test('allows passing custom reducer creators, which can add actions and case reducers', () => {
+      expect(() =>
+        createSlice({
+          name: 'loader',
+          initialState: {} as Partial<Record<string, true>>,
+          reducers: () => ({
+            addLoader: loaderCreator.create({}),
+          }),
+        }),
+      ).toThrowErrorMatchingInlineSnapshot(`[Error: Unsupported reducer type: Symbol(loaderCreatorType)]`)
       const createAppSlice = buildCreateSlice({
         creators: { loader: loaderCreator },
       })

From c2523fcc45101f7d52d109cc3cd9361f4bdfe75a Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sat, 6 Apr 2024 21:52:29 +0100
Subject: [PATCH 156/178] golf

---
 packages/toolkit/src/createSlice.ts | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 087784c72e..3c701abf9e 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -1045,11 +1045,11 @@ export function buildCreateSlice<
           reducerPath,
           type: getType(name, reducerName),
         }
-        const handler =
+        const { handle } =
           'reducer' in reducerDefinition
             ? preparedReducerCreator
             : reducerCreator
-        handler.handle(
+        handle(
           reducerDetails,
           reducerDefinition as any,
           getContext(reducerDetails),

From a9e3fa48e63c81ebf09493e8065f53309da316ce Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sat, 6 Apr 2024 22:32:37 +0100
Subject: [PATCH 157/178] make sure each creator symbol has a description

---
 docs/usage/custom-slice-creators.mdx | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/docs/usage/custom-slice-creators.mdx b/docs/usage/custom-slice-creators.mdx
index 530d02ec88..ac4051593d 100644
--- a/docs/usage/custom-slice-creators.mdx
+++ b/docs/usage/custom-slice-creators.mdx
@@ -390,7 +390,7 @@ const todoSlice = createSlice({
 The `type` property of the definition should be the same constant used for reducer definitions to be handled by this creator. To avoid collision, we recommend using Symbols for this. It's also used for defining/retrieving types - see [Typescript](#typescript).
 
 ```ts no-transpile
-const reducerCreatorType = Symbol()
+const reducerCreatorType = Symbol('reducerCreatorType')
 
 const reducerCreator: ReducerCreator<typeof reducerCreatorType> = {
   type: reducerCreatorType,
@@ -506,7 +506,7 @@ The Typescript system for custom slice creators uses a "creator registry" system
 Creators are registered by using module augmentation to add a new key (their unique `type`) to the `SliceReducerCreators` interface. The interface receives three type parameters (`State`, `CaseReducers` and `Name`), and each entry should use the `ReducerCreatorEntry` type utility.
 
 ```ts no-transpile
-const reducerCreatorType = Symbol()
+const reducerCreatorType = Symbol('reducerCreatorType')
 
 declare module '@reduxjs/toolkit' {
   export interface SliceReducerCreators<
@@ -548,7 +548,7 @@ Assuming the creator is called as `create.yourCreator()`, the `this` value for t
 However, this should be specifically included in the function signature, so Typescript can warn if called with an incorrect context (for example, if the user destructures from the `create` value).
 
 ```ts no-transpile
-const batchedCreatorType = Symbol()
+const batchedCreatorType = Symbol('batchedCreatorType')
 
 declare module '@reduxjs/toolkit' {
   export interface SliceReducerCreators<
@@ -587,7 +587,7 @@ The second argument to the `ReducerCreators` type is a map from creator names to
 Sometimes it's useful to have a reducer creator that only works with a specific state shape. You can ensure the creator is only callable if the state matches, using a conditional type:
 
 ```ts no-transpile
-const loaderCreatorType = Symbol()
+const loaderCreatorType = Symbol('loaderCreatorType')
 
 declare module '@reduxjs/toolkit' {
   export interface SliceReducerCreators<
@@ -614,7 +614,7 @@ Any creators that evaluate to the `never` type are omitted from the final `creat
 An alternative would be just using that required type _as_ the `State` type for the reducer definitions, so Typescript then complains when the creator is used.
 
 ```ts no-transpile
-const loaderCreatorType = Symbol()
+const loaderCreatorType = Symbol('loaderCreatorType')
 
 declare module '@reduxjs/toolkit' {
   export interface SliceReducerCreators<
@@ -656,7 +656,7 @@ In order to ensure that the definitions are correctly filtered to only include t
 For example, with (a simplified version of) the `asyncThunk` creator:
 
 ```ts no-transpile
-const asyncThunkCreatorType = Symbol()
+const asyncThunkCreatorType = Symbol('asyncThunkCreatorType')
 
 declare module '@reduxjs/toolkit' {
   export interface SliceReducerCreators<
@@ -694,7 +694,7 @@ Similar to `actions`, except for `slice.caseReducers`.
 For example, with the `preparedReducer` creator:
 
 ```ts no-transpile
-const preparedReducerType = Symbol()
+const preparedReducerType = Symbol('preparedReducerType')
 
 declare module '@reduxjs/toolkit' {
   export interface SliceReducerCreators<
@@ -771,7 +771,7 @@ One example would be reusable toast logic; you could have a reducer creator that
 
 ```ts no-transpile
 // create the unique type
-const toastCreatorType = Symbol()
+const toastCreatorType = Symbol('toastCreatorType')
 
 interface Toast {
   message: string
@@ -926,7 +926,7 @@ A creator could also return multiple definitions, which would then be spread int
 One example could be returning some pagination related reducers.
 
 ```ts no-transpile
-const paginationCreatorType = Symbol()
+const paginationCreatorType = Symbol('paginationCreatorType')
 
 interface PaginationState {
   page: number
@@ -990,7 +990,7 @@ const { prevPage, nextPage, goToPage, toggleLoading } = paginationSlice.actions
 A creator could return a mix of reducer definitions for itself and other creators to handle:
 
 ```ts no-transpile
-const historyCreatorType = Symbol()
+const historyCreatorType = Symbol('historyCreatorType')
 
 interface PatchesState {
   undo: Patch[]
@@ -1111,7 +1111,7 @@ Following on from the `HistoryState` example above, it would be useful to make s
 Fortunately, this is possible with a creator:
 
 ```ts no-transpile
-const undoableCreatorType = Symbol()
+const undoableCreatorType = Symbol('undoableCreatorType')
 
 interface UndoableMeta {
   undoable?: boolean

From 02bbfba8630e2c5e474b88f1274c8fbcd5b0bbb0 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sat, 6 Apr 2024 22:34:23 +0100
Subject: [PATCH 158/178] simplify GetCaseReducers

---
 packages/toolkit/src/createSlice.ts | 23 ++++-------------------
 1 file changed, 4 insertions(+), 19 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 3c701abf9e..f89049f83c 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -507,17 +507,8 @@ type CreatorCallback<
 ) => Record<string, ReducerDefinition>
 
 type GetCaseReducers<
-  State,
-  Name extends string,
-  ReducerPath extends string,
-  CreatorMap extends Record<string, RegisteredReducerType>,
-  CR extends
-    | SliceCaseReducers<State>
-    | CreatorCallback<State, Name, ReducerPath, CreatorMap>,
-> =
-  CR extends CreatorCallback<State, Name, ReducerPath, CreatorMap>
-    ? ReturnType<CR>
-    : CR
+  CR extends SliceCaseReducers<any> | CreatorCallback<any, any, any, any>,
+> = CR extends CreatorCallback<any, any, any, any> ? ReturnType<CR> : CR
 
 /**
  * Options for `createSlice()`.
@@ -916,13 +907,7 @@ export function buildCreateSlice<
       Selectors,
       CreatorMap
     >,
-  ): Slice<
-    State,
-    GetCaseReducers<State, Name, ReducerPath, CreatorMap, CaseReducers>,
-    Name,
-    ReducerPath,
-    Selectors
-  > {
+  ): Slice<State, GetCaseReducers<CaseReducers>, Name, ReducerPath, Selectors> {
     const { name, reducerPath = name as unknown as ReducerPath } = options
     if (!name) {
       throw new Error('`name` is a required option for createSlice')
@@ -1119,7 +1104,7 @@ export function buildCreateSlice<
     ): Pick<
       Slice<
         State,
-        GetCaseReducers<State, Name, ReducerPath, CreatorMap, CaseReducers>,
+        GetCaseReducers<CaseReducers>,
         Name,
         CurrentReducerPath,
         Selectors

From 0133506a7cc2003c57a240ed416df79f35a7d9c2 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Sat, 6 Apr 2024 22:35:46 +0100
Subject: [PATCH 159/178] use utility more

---
 packages/toolkit/src/createSlice.ts | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index f89049f83c..1308818891 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -1160,9 +1160,7 @@ export function buildCreateSlice<
 
     const slice: Slice<
       State,
-      CaseReducers extends CreatorCallback<State, Name, ReducerPath, CreatorMap>
-        ? ReturnType<CaseReducers>
-        : CaseReducers,
+      GetCaseReducers<CaseReducers>,
       Name,
       ReducerPath,
       Selectors

From f86a6d5084e268b23577beea22c66d9ebc767935 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Mon, 8 Apr 2024 19:03:46 +0100
Subject: [PATCH 160/178] unsimplify GetCaseReducers because it broke
 everything

---
 packages/toolkit/src/createSlice.ts | 23 ++++++++++++++++++-----
 1 file changed, 18 insertions(+), 5 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 1308818891..e1067adb02 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -507,8 +507,15 @@ type CreatorCallback<
 ) => Record<string, ReducerDefinition>
 
 type GetCaseReducers<
-  CR extends SliceCaseReducers<any> | CreatorCallback<any, any, any, any>,
-> = CR extends CreatorCallback<any, any, any, any> ? ReturnType<CR> : CR
+  State,
+  Name extends string,
+  ReducerPath extends string,
+  CreatorMap extends Record<string, RegisteredReducerType>,
+  CR extends SliceCaseReducers<State> | CreatorCallback<State, any, any, any>,
+> =
+  CR extends CreatorCallback<State, Name, ReducerPath, CreatorMap>
+    ? ReturnType<CR>
+    : CR
 
 /**
  * Options for `createSlice()`.
@@ -907,7 +914,13 @@ export function buildCreateSlice<
       Selectors,
       CreatorMap
     >,
-  ): Slice<State, GetCaseReducers<CaseReducers>, Name, ReducerPath, Selectors> {
+  ): Slice<
+    State,
+    GetCaseReducers<State, Name, ReducerPath, CreatorMap, CaseReducers>,
+    Name,
+    ReducerPath,
+    Selectors
+  > {
     const { name, reducerPath = name as unknown as ReducerPath } = options
     if (!name) {
       throw new Error('`name` is a required option for createSlice')
@@ -1104,7 +1117,7 @@ export function buildCreateSlice<
     ): Pick<
       Slice<
         State,
-        GetCaseReducers<CaseReducers>,
+        GetCaseReducers<State, Name, ReducerPath, CreatorMap, CaseReducers>,
         Name,
         CurrentReducerPath,
         Selectors
@@ -1160,7 +1173,7 @@ export function buildCreateSlice<
 
     const slice: Slice<
       State,
-      GetCaseReducers<CaseReducers>,
+      GetCaseReducers<State, Name, ReducerPath, CreatorMap, CaseReducers>,
       Name,
       ReducerPath,
       Selectors

From 52d8d12ecb8bd9f05c6910dda1217a1ce688d9fb Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Mon, 8 Apr 2024 19:12:25 +0100
Subject: [PATCH 161/178] golf

---
 packages/toolkit/src/createSlice.ts | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index e1067adb02..2deb5d0faf 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -1184,8 +1184,10 @@ export function buildCreateSlice<
       caseReducers: internalContext.sliceCaseReducersByName as any,
       getInitialState,
       ...makeSelectorProps(reducerPath),
-      injectInto(injectable, { reducerPath: pathOpt, ...config } = {}) {
-        const newReducerPath = pathOpt ?? reducerPath
+      injectInto(
+        injectable,
+        { reducerPath: newReducerPath = reducerPath, ...config } = {},
+      ) {
         injectable.inject({ reducerPath: newReducerPath, reducer }, config)
         return {
           ...slice,

From 5e256a6164d0235b092731fe568b9bc451aa2ab3 Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Mon, 8 Apr 2024 20:43:16 +0100
Subject: [PATCH 162/178] remove unnecessary cast

---
 packages/toolkit/src/createSlice.ts | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 2deb5d0faf..d359e8ebc9 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -1017,8 +1017,8 @@ export function buildCreateSlice<
             'Please use reducer creators passed to callback. Each reducer definition must have a `_reducerDefinitionType` property indicating which handler to use.',
           )
         }
-        const handler = handlers[type as RegisteredReducerType]
-        if (!handler) {
+        const handle = handlers[type]
+        if (!handle) {
           throw new Error(`Unsupported reducer type: ${String(type)}`)
         }
         const reducerDetails: ReducerDetails = {
@@ -1027,7 +1027,7 @@ export function buildCreateSlice<
           reducerPath,
           type: getType(name, reducerName),
         }
-        handler(
+        handle(
           reducerDetails,
           reducerDefinition as any,
           getContext(reducerDetails),

From a84abbdf1e9b3ecee342703ffc1eff401d5d23ad Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Mon, 8 Apr 2024 22:14:56 +0100
Subject: [PATCH 163/178] expand upon creator docs further

---
 docs/usage/custom-slice-creators.mdx | 125 ++++++++++++++++++++++++---
 1 file changed, 114 insertions(+), 11 deletions(-)

diff --git a/docs/usage/custom-slice-creators.mdx b/docs/usage/custom-slice-creators.mdx
index ac4051593d..a77454705f 100644
--- a/docs/usage/custom-slice-creators.mdx
+++ b/docs/usage/custom-slice-creators.mdx
@@ -445,6 +445,8 @@ The same as [`addCase`](../api/createReducer#builderaddcase) for `createReducer`
 ```ts no-transpile
 const action = createAction(type)
 context.addCase(action, reducer)
+// or
+context.addCase(type, reducer)
 ```
 
 #### `addMatcher`
@@ -465,6 +467,12 @@ const action = createAction(type)
 context.exposeAction(action)
 ```
 
+:::caution
+
+`exposeAction` should only be called once (at maximum) within a `handle` callback - the same applies to `exposeCaseReducer`.
+
+:::
+
 #### `exposeCaseReducer`
 
 Attaches a value to `slice.caseReducers[reducerName]`.
@@ -478,12 +486,12 @@ context.exposeCaseReducer(reducer)
 Returns the initial state value for the slice. If a lazy state initializer has been provided, it will be called and a fresh value returned.
 
 ```ts no-transpile
-const resetAction = createAction(type)
+const resetAction = createAction(type + '/reset')
 const resetReducer = () => context.getInitialState()
 context
   .addCase(resetAction, resetReducer)
-  .exposeAction(resetAction)
-  .exposeCaseReducer(resetReducer)
+  .exposeAction({ reset: resetAction })
+  .exposeCaseReducer({ reset: resetReducer })
 ```
 
 #### `selectSlice`
@@ -503,7 +511,7 @@ const aThunk =
 
 The Typescript system for custom slice creators uses a "creator registry" system similar to the module system for [RTK Query](/rtk-query/usage/customizing-create-api#creating-your-own-module).
 
-Creators are registered by using module augmentation to add a new key (their unique `type`) to the `SliceReducerCreators` interface. The interface receives three type parameters (`State`, `CaseReducers` and `Name`), and each entry should use the `ReducerCreatorEntry` type utility.
+Creators are registered by using module augmentation to add a new key (their unique `type`) to the `SliceReducerCreators` interface. Each entry should use the `ReducerCreatorEntry` type utility.
 
 ```ts no-transpile
 const reducerCreatorType = Symbol('reducerCreatorType')
@@ -535,9 +543,9 @@ The `ReducerCreatorEntry<Create, Exposes>` utility has two type parameters:
 
 The signature of the `create` method of the creator definition.
 
-:::caution `CaseReducers` and `Name`
+:::caution `CaseReducers`
 
-Your `Create` type should not depend on the `CaseReducers` and `Name` type parameters, as these will not yet exist when the creator is being called.
+Your `Create` type should not depend on the `CaseReducers` type parameter, as these will not yet exist when the creator is being called.
 
 :::
 
@@ -580,6 +588,39 @@ const batchedCreator: ReducerCreator<typeof batchedCreatorType> = {
 
 The second argument to the `ReducerCreators` type is a map from creator names to types, which you should supply if you're expecting to use any custom creators (anything other than `reducer` and `preparedReducer`) within your own creator. For example, `ReducerCreators<State, { asyncThunk: typeof asyncThunkCreator.type }>` would allow you to call `this.asyncThunk`.
 
+Alternatively, you can import the other creator's definition and use it directly.
+
+```ts no-transpile
+import { preparedReducerCreator } from '@reduxjs/toolkit'
+
+const batchedCreatorType = Symbol('batchedCreatorType')
+
+declare module '@reduxjs/toolkit' {
+  export interface SliceReducerCreators<
+    State,
+    CaseReducers extends CreatorCaseReducers<State>,
+    Name extends string,
+    ReducerPath extends string,
+  > {
+    [batchedCreatorType]: ReducerCreatorEntry<
+      <Payload>(
+        reducer: CaseReducer<State, PayloadAction<Payload>>,
+      ) => PreparedCaseReducerDefinition<
+        State,
+        (payload: Payload) => { payload: Payload; meta: unknown }
+      >
+    >
+  }
+}
+
+const batchedCreator: ReducerCreator<typeof batchedCreatorType> = {
+  type: batchedCreatorType,
+  create(reducer) {
+    return preparedReducerCreator.create(prepareAutoBatched(), reducer)
+  },
+}
+```
+
 :::
 
 :::note Ensuring compatible state
@@ -653,6 +694,8 @@ In order to ensure that the definitions are correctly filtered to only include t
 }
 ```
 
+To relate back to the context methods, it should describe what you will pass to `context.exposeAction` from a handler.
+
 For example, with (a simplified version of) the `asyncThunk` creator:
 
 ```ts no-transpile
@@ -691,6 +734,8 @@ declare module '@reduxjs/toolkit' {
 
 Similar to `actions`, except for `slice.caseReducers`.
 
+It describes what you will pass to `context.exposeCaseReducer` from a handler.
+
 For example, with the `preparedReducer` creator:
 
 ```ts no-transpile
@@ -956,6 +1001,8 @@ const paginationCreator: ReducerCreator<typeof paginationCreatorType> = {
   type: paginationCreatorType,
   create() {
     return {
+      // calling `this.reducer` assumes we'll be calling the creator as `create.paginationMethods()`
+      // if we don't want this assumption, we could use `reducerCreator.create` instead
       prevPage: this.reducer((state: PaginationState) => {
         state.page--
       }),
@@ -1011,7 +1058,7 @@ declare module '@reduxjs/toolkit' {
     ReducerPath extends string,
   > {
     [historyCreatorType]: ReducerCreatorEntry<
-      // make sure the creator is only called when state is compatibleState extends HistoryState<unknown>
+      // make sure the creator is only called when state is compatible
       State extends HistoryState<any>
         ? (this: ReducerCreators<State>) => {
             undo: CaseReducerDefinition<State, PayloadAction>
@@ -1063,18 +1110,22 @@ const historyCreator: ReducerCreator<typeof historyCreatorType> = {
           state.past.push(historyEntry)
         }
       }),
+      // highlight-start
+      // here we're creating a reducer definition that our `handle` method will be called with
       reset: {
         _reducerDefinitionType: historyCreatorType,
         type: 'reset',
       },
+      // highlight-end
     }
   },
   handle(details, definition, context) {
     if (definition.type !== 'reset') {
       throw new Error('Unrecognised definition type: ' + definition.type)
     }
-    // use the normal reducer creator to create a case reducer and action creator
     const resetReducer = () => context.getInitialState()
+    // you can call other creators' `handle` methods if needed
+    // here we're reusing `reducerCreator` to get the expected behaviour of making an action creator for our reducer
     reducerCreator.handle(details, reducerCreator.create(resetReducer), context)
   },
 }
@@ -1151,7 +1202,7 @@ const undoableCreator: ReducerCreator<typeof undoableCreatorType> = {
       reducer: CaseReducer<any, A>,
     ): CaseReducer<HistoryState<any>, A> {
       return (state, action) => {
-        const [nextState, redoPatch, undoPatch] = produceWithPatches(
+        const [nextState, redoPatches, undoPatches] = produceWithPatches(
           state,
           (draft) => {
             const result = reducer(draft.present, action)
@@ -1165,8 +1216,8 @@ const undoableCreator: ReducerCreator<typeof undoableCreatorType> = {
         if (undoable) {
           finalState = createNextState(finalState, (draft) => {
             draft.past.push({
-              undo: undoPatch,
-              redo: redoPatch,
+              undo: undoPatches,
+              redo: redoPatches,
             })
             draft.future = []
           })
@@ -1221,3 +1272,55 @@ const postSliceWithHistory = createAppSlice({
 const { undo, redo, reset, updateTitle, togglePinned } =
   postSliceWithHistory.actions
 ```
+
+:::tip `history-adapter`
+
+This example is a somewhat simplified version of the [`history-adapter`](https://www.npmjs.com/package/history-adapter) package, which provides a `createHistoryAdapter` utility that can be used to add undo/redo functionality to a slice.
+
+```ts no-transpile
+import {
+  createHistoryAdapter,
+  historyMethodsCreator,
+  undoableCreatorsCreator,
+} from 'history-adapter/redux'
+
+const createAppSlice = buildCreateSlice({
+  creators: {
+    historyMethods: historyMethodsCreator,
+    undoableCreators: undoableCreatorsCreator,
+  },
+})
+
+const postHistoryAdapter = createHistoryAdapter<Post>({ limit: 5 })
+
+const postSliceWithHistory = createAppSlice({
+  name: 'post',
+  initialState: postHistoryAdapter.getInitialState({
+    title: '',
+    pinned: false,
+  }),
+  reducers: (create) => {
+    const createUndoable = create.undoableCreators(postHistoryAdapter)
+    return {
+      ...create.historyMethods(postHistoryAdapter),
+      updateTitle: createUndoable.preparedReducer(
+        postHistoryAdapter.withPayload<string>(),
+        (state, action) => {
+          state.title = action.payload
+        },
+      ),
+      togglePinned: createUndoable.preparedReducer(
+        postHistoryAdapter.withoutPayload(),
+        (state, action) => {
+          state.pinned = !state.pinned
+        },
+      ),
+    }
+  },
+})
+
+const { undo, redo, reset, updateTitle, togglePinned } =
+  postSliceWithHistory.actions
+```
+
+:::

From f8ad5171b5acf1665e65ca942d66e55795e37967 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.durrant@marketdojo.com>
Date: Wed, 22 May 2024 11:13:47 +0100
Subject: [PATCH 164/178] move async thunk creator module augmentation

---
 packages/toolkit/src/asyncThunkCreator.ts | 48 ++++++++++++++++++++++-
 packages/toolkit/src/createSlice.ts       | 35 -----------------
 2 files changed, 47 insertions(+), 36 deletions(-)

diff --git a/packages/toolkit/src/asyncThunkCreator.ts b/packages/toolkit/src/asyncThunkCreator.ts
index d0ae93ed75..52651b7a0c 100644
--- a/packages/toolkit/src/asyncThunkCreator.ts
+++ b/packages/toolkit/src/asyncThunkCreator.ts
@@ -7,8 +7,54 @@ import type {
 } from './createAsyncThunk'
 import { createAsyncThunk } from './createAsyncThunk'
 import type { CaseReducer } from './createReducer'
-import type { ReducerCreator, ReducerDefinition } from './createSlice'
+import type {
+  CreatorCaseReducers,
+  ReducerCreator,
+  ReducerCreatorEntry,
+  ReducerDefinition,
+} from './createSlice'
 import { ReducerType } from './createSlice'
+import type { Id } from './tsHelpers'
+
+declare module '@reduxjs/toolkit' {
+  export interface SliceReducerCreators<
+    State,
+    CaseReducers extends CreatorCaseReducers<State>,
+    Name extends string,
+    ReducerPath extends string,
+  > {
+    [ReducerType.asyncThunk]: ReducerCreatorEntry<
+      AsyncThunkCreator<State>,
+      {
+        actions: {
+          [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition<
+            State,
+            infer ThunkArg,
+            infer Returned,
+            infer ThunkApiConfig
+          >
+            ? AsyncThunk<Returned, ThunkArg, ThunkApiConfig>
+            : never
+        }
+        caseReducers: {
+          [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition<
+            State,
+            any,
+            any,
+            any
+          >
+            ? Id<
+                Pick<
+                  Required<CaseReducers[ReducerName]>,
+                  'fulfilled' | 'rejected' | 'pending' | 'settled'
+                >
+              >
+            : never
+        }
+      }
+    >
+  }
+}
 
 export interface AsyncThunkSliceReducerConfig<
   State,
diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index a8819bc1f7..6bc6f3b455 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -1,9 +1,5 @@
 import type { Action, UnknownAction, Reducer } from 'redux'
 import type { Selector } from 'reselect'
-import type {
-  AsyncThunkCreator,
-  AsyncThunkSliceReducerDefinition,
-} from './asyncThunkCreator'
 import type {
   ActionCreatorWithoutPayload,
   PayloadAction,
@@ -12,7 +8,6 @@ import type {
   _ActionCreatorWithPreparedPayload,
 } from './createAction'
 import { createAction } from './createAction'
-import type { AsyncThunk } from './createAsyncThunk'
 import type {
   ActionMatcherDescriptionCollection,
   CaseReducer,
@@ -135,36 +130,6 @@ export interface SliceReducerCreators<
       }
     }
   >
-  [ReducerType.asyncThunk]: ReducerCreatorEntry<
-    AsyncThunkCreator<State>,
-    {
-      actions: {
-        [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition<
-          State,
-          infer ThunkArg,
-          infer Returned,
-          infer ThunkApiConfig
-        >
-          ? AsyncThunk<Returned, ThunkArg, ThunkApiConfig>
-          : never
-      }
-      caseReducers: {
-        [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition<
-          State,
-          any,
-          any,
-          any
-        >
-          ? Id<
-              Pick<
-                Required<CaseReducers[ReducerName]>,
-                'fulfilled' | 'rejected' | 'pending' | 'settled'
-              >
-            >
-          : never
-      }
-    }
-  >
 }
 
 export type ReducerCreators<

From cdf4498193389df814c9a9608d92f2cd8afd1bc4 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.durrant@marketdojo.com>
Date: Wed, 22 May 2024 11:32:45 +0100
Subject: [PATCH 165/178] try something different

---
 packages/toolkit/src/asyncThunkCreator.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/toolkit/src/asyncThunkCreator.ts b/packages/toolkit/src/asyncThunkCreator.ts
index 52651b7a0c..e0ac322c8c 100644
--- a/packages/toolkit/src/asyncThunkCreator.ts
+++ b/packages/toolkit/src/asyncThunkCreator.ts
@@ -16,7 +16,7 @@ import type {
 import { ReducerType } from './createSlice'
 import type { Id } from './tsHelpers'
 
-declare module '@reduxjs/toolkit' {
+declare module './createSlice' {
   export interface SliceReducerCreators<
     State,
     CaseReducers extends CreatorCaseReducers<State>,

From 2d08f5d3a9ffdac2857ae79b1052f8a6b1d008af Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.durrant@marketdojo.com>
Date: Wed, 29 May 2024 18:00:43 +0100
Subject: [PATCH 166/178] tweak example

---
 docs/usage/custom-slice-creators.mdx | 29 ++++++++++++++--------------
 1 file changed, 15 insertions(+), 14 deletions(-)

diff --git a/docs/usage/custom-slice-creators.mdx b/docs/usage/custom-slice-creators.mdx
index a77454705f..a28f73e0e6 100644
--- a/docs/usage/custom-slice-creators.mdx
+++ b/docs/usage/custom-slice-creators.mdx
@@ -591,7 +591,10 @@ The second argument to the `ReducerCreators` type is a map from creator names to
 Alternatively, you can import the other creator's definition and use it directly.
 
 ```ts no-transpile
-import { preparedReducerCreator } from '@reduxjs/toolkit'
+import {
+  preparedReducerCreator,
+  PreparedCaseReducerDefinition,
+} from '@reduxjs/toolkit'
 
 const batchedCreatorType = Symbol('batchedCreatorType')
 
@@ -755,25 +758,23 @@ declare module '@reduxjs/toolkit' {
       ) => PreparedCaseReducerDefinition<State, Prepare>,
       {
         actions: {
-          [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends ReducerDefinition<
-            typeof preparedReducerType
+          [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends PreparedCaseReducerDefinition<
+            any,
+            any
           >
-            ? CaseReducers[ReducerName] extends { prepare: any }
-              ? ActionCreatorForCaseReducerWithPrepare<
-                  CaseReducers[ReducerName],
-                  SliceActionType<Name, ReducerName>
-                >
-              : never
+            ? ActionCreatorForCaseReducerWithPrepare<
+                CaseReducers[ReducerName],
+                SliceActionType<Name, ReducerName>
+              >
             : never
         }
         // highlight-start
         caseReducers: {
-          [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends ReducerDefinition<
-            typeof preparedReducerType
+          [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends PreparedCaseReducerDefinition<
+            any,
+            any
           >
-            ? CaseReducers[ReducerName] extends { reducer: infer Reducer }
-              ? Reducer
-              : never
+            ? CaseReducers[ReducerName]['reducer']
             : never
         }
         // highlight-end

From 6226c1de9ca743e733e5086fbf94823a5a792744 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.durrant@marketdojo.com>
Date: Wed, 29 May 2024 18:10:59 +0100
Subject: [PATCH 167/178] add note about exposing multiple values per reducer
 def

---
 docs/usage/custom-slice-creators.mdx | 58 +++++++++++++++++++++++++++-
 1 file changed, 57 insertions(+), 1 deletion(-)

diff --git a/docs/usage/custom-slice-creators.mdx b/docs/usage/custom-slice-creators.mdx
index a28f73e0e6..427fde5188 100644
--- a/docs/usage/custom-slice-creators.mdx
+++ b/docs/usage/custom-slice-creators.mdx
@@ -449,6 +449,8 @@ context.addCase(action, reducer)
 context.addCase(type, reducer)
 ```
 
+Returns the context object to allow chaining.
+
 #### `addMatcher`
 
 The same as [`addMatcher`](../api/createReducer#builderaddmatcher) for `createReducer` and `extraReducers`. Adds a case reducer which will be called when a given matcher returns true.
@@ -458,6 +460,8 @@ const matcher = isAnyOf(action, action2)
 context.addMatcher(matcher, reducer)
 ```
 
+Returns the context object to allow chaining.
+
 #### `exposeAction`
 
 Attaches a value to `slice.actions[reducerName]`.
@@ -467,9 +471,18 @@ const action = createAction(type)
 context.exposeAction(action)
 ```
 
+Returns the context object to allow chaining.
+
 :::caution
 
-`exposeAction` should only be called once (at maximum) within a `handle` callback - the same applies to `exposeCaseReducer`.
+`exposeAction` should only be called once (at maximum) within a `handle` callback.
+
+If you want to expose multiple values for a given case reducer's actions, you can pass an object to `exposeAction`.
+
+```ts no-transpile
+context.exposeAction({ hidden: hiddenAction, shown: shownAction })
+// available as slice.actions[reducerName].hidden and slice.actions[reducerName].shown
+```
 
 :::
 
@@ -481,6 +494,49 @@ Attaches a value to `slice.caseReducers[reducerName]`.
 context.exposeCaseReducer(reducer)
 ```
 
+Returns the context object to allow chaining.
+
+:::caution
+
+Just like `exposeAction`, `exposeCaseReducer` should only be called once (at maximum) within a `handle` callback.
+
+If you want to expose multiple values for a given case reducer definition, you can pass an object to `exposeCaseReducer`.
+
+```ts no-transpile
+context.exposeCaseReducer({
+  hidden: config.hidden || noop,
+  shown: config.shown || noop,
+})
+// available as slice.caseReducers[reducerName].hidden and slice.caseReducers[reducerName].shown
+```
+
+You can see an example of this with the `asyncThunk` creator in the [RTK creators](#rtk-creators) section, which exposes case reducers for each of the lifecycle actions.
+
+```ts no-transpile
+const asyncThunkCreator: ReducerCreator<typeof asyncThunkCreatorType> = {
+  type: asyncThunkCreatorType,
+  create(payloadCreator, config) {
+    return {
+      _reducerDefinitionType: asyncThunkCreatorType,
+      payloadCreator,
+      config,
+    }
+  },
+  handle({ type }, definition, context) {
+    const { payloadCreator, config } = definition
+    const thunk = createAsyncThunk(type, payloadCreator, config)
+    context.exposeAction(thunk).exposeCaseReducer({
+      pending: config.pending || noop,
+      rejected: config.rejected || noop,
+      fulfilled: config.fulfilled || noop,
+      settled: config.settled || noop,
+    })
+  },
+}
+```
+
+:::
+
 #### `getInitialState`
 
 Returns the initial state value for the slice. If a lazy state initializer has been provided, it will be called and a fresh value returned.

From ce04544aa52842a835c81dbdba41dd62451f3d4f Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.durrant@marketdojo.com>
Date: Wed, 29 May 2024 18:15:17 +0100
Subject: [PATCH 168/178] add note about addMatcher order

---
 docs/usage/custom-slice-creators.mdx | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/docs/usage/custom-slice-creators.mdx b/docs/usage/custom-slice-creators.mdx
index 427fde5188..b4042aa973 100644
--- a/docs/usage/custom-slice-creators.mdx
+++ b/docs/usage/custom-slice-creators.mdx
@@ -462,6 +462,14 @@ context.addMatcher(matcher, reducer)
 
 Returns the context object to allow chaining.
 
+:::tip
+
+Unlike in `createReducer` and `extraReducers`, there is no requirement to call `addMatcher` _after_ `addCase` - the correct order will be applied when the reducer is built.
+
+You should still be aware that case reducers will be called before matcher reducers, if both match a given action.
+
+:::
+
 #### `exposeAction`
 
 Attaches a value to `slice.actions[reducerName]`.

From 32cdde8af82f48f2f2c3a459520f81e7fe6048ec Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Mon, 3 Jun 2024 22:52:00 +0100
Subject: [PATCH 169/178] limit recursion depth of definition extraction

---
 packages/toolkit/src/createSlice.ts | 82 +++++++++++++++--------------
 packages/toolkit/src/tsHelpers.ts   | 19 +++++++
 2 files changed, 61 insertions(+), 40 deletions(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 6bc6f3b455..87f62fcf4e 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -16,7 +16,15 @@ import type {
 import { createReducer, makeGetInitialState } from './createReducer'
 import type { ActionReducerMapBuilder, TypedActionCreator } from './mapBuilders'
 import { executeReducerBuilderCallback } from './mapBuilders'
-import type { CastAny, Id, TypeGuard, UnionToIntersection } from './tsHelpers'
+import type {
+  CastAny,
+  Id,
+  Increment,
+  IsAny,
+  OverloadedReturnType,
+  TypeGuard,
+  UnionToIntersection,
+} from './tsHelpers'
 import type { InjectConfig } from './combineSlices'
 import { emplace } from './utils'
 
@@ -265,54 +273,48 @@ export interface ReducerDetails {
   type: string
 }
 
-type RecursiveExtractDefinition<
-  Definitions,
+type DefinitionFromValue<
+  T extends object,
   Type extends RegisteredReducerType,
-> = CastAny<
-  | Extract<Definitions, ReducerDefinition<Type>>
-  | (Definitions extends object
-      ? {
-          [K in keyof Definitions]-?: RecursiveExtractDefinition<
-            Definitions[K],
-            Type
-          >
-        }[keyof Definitions]
-      : never),
-  never
->
+  RecursionDepth extends number = 0,
+> = RecursionDepth extends 5
+  ? never
+  : IsAny<
+      T,
+      never,
+      | Extract<T, ReducerDefinition<Type>>
+      | {
+          [K in keyof T]-?: T[K] extends object
+            ? DefinitionFromValue<T[K], Type, Increment<RecursionDepth>>
+            : never
+        }[keyof T]
+      | (T extends (...args: any[]) => object
+          ? DefinitionFromValue<
+              OverloadedReturnType<T>,
+              Type,
+              Increment<RecursionDepth>
+            >
+          : never)
+    >
 
 type ReducerDefinitionsForType<Type extends RegisteredReducerType> = {
-  [CreatorType in RegisteredReducerType]:
-    | RecursiveExtractDefinition<
-        ReturnType<
-          SliceReducerCreators<any, any, any, any>[CreatorType]['create']
-        >,
-        Type
-      >
-    | {
-        [K in keyof SliceReducerCreators<
-          any,
-          any,
-          any,
-          any
-        >[CreatorType]['create']]: SliceReducerCreators<
-          any,
-          any,
-          any,
-          any
-        >[CreatorType]['create'][K] extends (
-          ...args: any[]
-        ) => infer Definitions
-          ? RecursiveExtractDefinition<Definitions, Type>
-          : never
-      }[keyof SliceReducerCreators<any, any, any, any>[CreatorType]['create']]
+  [CreatorType in RegisteredReducerType]: DefinitionFromValue<
+    SliceReducerCreators<any, any, any, any>[CreatorType]['create'],
+    Type
+  >
 }[RegisteredReducerType]
 
 export type ReducerCreator<Type extends RegisteredReducerType> = {
   type: Type
   create: SliceReducerCreators<any, any, any, any>[Type]['create']
 } & (ReducerDefinitionsForType<Type> extends never
-  ? {}
+  ? {
+      handle?<State>(
+        details: ReducerDetails,
+        definition: unknown,
+        context: ReducerHandlingContext<State>,
+      ): void
+    }
   : {
       handle<State>(
         details: ReducerDetails,
diff --git a/packages/toolkit/src/tsHelpers.ts b/packages/toolkit/src/tsHelpers.ts
index 45c90aeaef..fc2070d80b 100644
--- a/packages/toolkit/src/tsHelpers.ts
+++ b/packages/toolkit/src/tsHelpers.ts
@@ -223,3 +223,22 @@ export function asSafePromise<Resolved, Rejected>(
 ) {
   return promise.catch(fallback) as SafePromise<Resolved | Rejected>
 }
+
+export type OverloadedReturnType<Fn extends (...args: any[]) => any> =
+  Fn extends {
+    (...args: any): infer R1
+    (...args: any): infer R2
+    (...args: any): infer R3
+  }
+    ? R1 | R2 | R3
+    : Fn extends {
+          (...args: any): infer R1
+          (...args: any): infer R2
+        }
+      ? R1 | R2
+      : ReturnType<Fn>
+
+export type Increment<
+  N extends number,
+  Acc extends 0[] = [],
+> = Acc['length'] extends N ? [...Acc, 0]['length'] : Increment<N, [...Acc, 0]>

From 0f01293437998eb180b1ca0d81e2658acca54a8a Mon Sep 17 00:00:00 2001
From: EskiMojo14 <ben.j.durrant@gmail.com>
Date: Mon, 3 Jun 2024 22:52:47 +0100
Subject: [PATCH 170/178] rm unused type

---
 packages/toolkit/src/createSlice.ts | 1 -
 1 file changed, 1 deletion(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 87f62fcf4e..58de4a56fd 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -17,7 +17,6 @@ import { createReducer, makeGetInitialState } from './createReducer'
 import type { ActionReducerMapBuilder, TypedActionCreator } from './mapBuilders'
 import { executeReducerBuilderCallback } from './mapBuilders'
 import type {
-  CastAny,
   Id,
   Increment,
   IsAny,

From 45c8b9ea4593c6f11d00908627b222822780de43 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.j.durrant@gmail.com>
Date: Tue, 4 Jun 2024 20:50:48 +0100
Subject: [PATCH 171/178] guard against strange behaviour of
 OverloadedReturnType in pre 5.3 versions

---
 packages/toolkit/src/tsHelpers.ts | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/packages/toolkit/src/tsHelpers.ts b/packages/toolkit/src/tsHelpers.ts
index fc2070d80b..15e187cfbb 100644
--- a/packages/toolkit/src/tsHelpers.ts
+++ b/packages/toolkit/src/tsHelpers.ts
@@ -224,18 +224,20 @@ export function asSafePromise<Resolved, Rejected>(
   return promise.catch(fallback) as SafePromise<Resolved | Rejected>
 }
 
+type NotUnknown<T> = IsUnknown<T, never, T>
+
 export type OverloadedReturnType<Fn extends (...args: any[]) => any> =
   Fn extends {
-    (...args: any): infer R1
-    (...args: any): infer R2
-    (...args: any): infer R3
+    (...args: any[]): infer R1
+    (...args: any[]): infer R2
+    (...args: any[]): infer R3
   }
-    ? R1 | R2 | R3
+    ? NotUnknown<R1> | NotUnknown<R2> | NotUnknown<R3>
     : Fn extends {
-          (...args: any): infer R1
-          (...args: any): infer R2
+          (...args: any[]): infer R1
+          (...args: any[]): infer R2
         }
-      ? R1 | R2
+      ? NotUnknown<R1> | NotUnknown<R2>
       : ReturnType<Fn>
 
 export type Increment<

From 99c0c7f213714ed4e9549baf09bdfef9e38a7e5e Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.j.durrant@gmail.com>
Date: Tue, 4 Jun 2024 21:07:37 +0100
Subject: [PATCH 172/178] condense OverloadedReturnType since first conditional
 is always hit

---
 packages/toolkit/src/tsHelpers.ts | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/packages/toolkit/src/tsHelpers.ts b/packages/toolkit/src/tsHelpers.ts
index 15e187cfbb..ad60b59a0d 100644
--- a/packages/toolkit/src/tsHelpers.ts
+++ b/packages/toolkit/src/tsHelpers.ts
@@ -231,14 +231,16 @@ export type OverloadedReturnType<Fn extends (...args: any[]) => any> =
     (...args: any[]): infer R1
     (...args: any[]): infer R2
     (...args: any[]): infer R3
+    (...args: any[]): infer R4
+    (...args: any[]): infer R5
   }
-    ? NotUnknown<R1> | NotUnknown<R2> | NotUnknown<R3>
-    : Fn extends {
-          (...args: any[]): infer R1
-          (...args: any[]): infer R2
-        }
-      ? NotUnknown<R1> | NotUnknown<R2>
-      : ReturnType<Fn>
+    ?
+        | NotUnknown<R1>
+        | NotUnknown<R2>
+        | NotUnknown<R3>
+        | NotUnknown<R4>
+        | NotUnknown<R5>
+    : ReturnType<Fn>
 
 export type Increment<
   N extends number,

From 62e96d5f47e9326cb85754d3505916fef2708ea3 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.durrant@marketdojo.com>
Date: Mon, 10 Jun 2024 15:08:51 +0100
Subject: [PATCH 173/178] remove note re: addMatcher order as it doesn't apply

---
 packages/toolkit/src/createSlice.ts | 1 -
 1 file changed, 1 deletion(-)

diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 58de4a56fd..4235bb57cf 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -206,7 +206,6 @@ export interface ReducerHandlingContext<State> {
    * @remarks
    * If multiple matcher reducers match, all of them will be executed in the order
    * they were defined in - even if a case reducer already matched.
-   * All calls to `builder.addMatcher` must come after any calls to `builder.addCase` and before any calls to `builder.addDefaultCase`.
    * @param matcher - A matcher function. In TypeScript, this should be a [type predicate](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates)
    *   function
    * @param reducer - The actual case reducer function.

From a12524af49fda2ff1a1d9f840dc926afb98cb26f Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.durrant@marketdojo.com>
Date: Fri, 14 Jun 2024 11:42:37 +0100
Subject: [PATCH 174/178] remove requirement for create to be a function

---
 docs/usage/custom-slice-creators.mdx | 8 ++++----
 packages/toolkit/src/createSlice.ts  | 6 +++---
 2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/docs/usage/custom-slice-creators.mdx b/docs/usage/custom-slice-creators.mdx
index b4042aa973..6cfbbdd0b1 100644
--- a/docs/usage/custom-slice-creators.mdx
+++ b/docs/usage/custom-slice-creators.mdx
@@ -363,7 +363,7 @@ Typically a creator will return a [single reducer definition](#single-definition
 
 ### Creator definitions
 
-A creator definition contains the actual runtime logic for that creator. It's an object with a `type` property, a `create` method, and an optional `handle` method.
+A creator definition contains the actual runtime logic for that creator. It's an object with a `type` property, a `create` value (typically a function or set of functions), and an optional `handle` method.
 
 It's passed to [`buildCreateSlice`](../api/createSlice#buildcreateslice) as part of the `creators` object, and the name used when calling `buildCreateSlice` will be the key the creator is nested under in the `create` object.
 
@@ -413,9 +413,9 @@ const reducerCreator: ReducerCreator<typeof reducerCreatorType> = {
 
 #### `create`
 
-The `create` method is the function that will be attached to the `create` object, before it's passed to the `reducers` callback.
+The `create` value will be attached to the `create` object, before it's passed to the `reducers` callback.
 
-Because it's a function, the `this` value will be the final `create` object when called (assuming a `create.creator()` call). It also could have additional methods attached.
+If it's a function, the `this` value will be the final `create` object when called (assuming a `create.creator()` call). It also could have additional methods attached, or be an object with methods.
 
 See the [Further examples](#further-examples) section for some examples of these.
 
@@ -605,7 +605,7 @@ The `ReducerCreatorEntry<Create, Exposes>` utility has two type parameters:
 
 #### `Create`
 
-The signature of the `create` method of the creator definition.
+The type of the `create` value of the creator definition (typically a function signature).
 
 :::caution `CaseReducers`
 
diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 4235bb57cf..8f122e6729 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -47,7 +47,7 @@ export interface ReducerDefinition<
 }
 
 export type ReducerCreatorEntry<
-  Create extends (...args: any[]) => any,
+  Create,
   Exposes extends {
     actions?: Record<string, unknown>
     caseReducers?: Record<string, unknown>
@@ -160,14 +160,14 @@ export type ReducerCreators<
 } & {
   [CreatorName in keyof CreatorMap as SliceReducerCreators<
     State,
-    any,
+    never,
     Name,
     ReducerPath
   >[CreatorMap[CreatorName]]['create'] extends never
     ? never
     : CreatorName]: SliceReducerCreators<
     State,
-    any,
+    never,
     Name,
     ReducerPath
   >[CreatorMap[CreatorName]]['create']

From e99e0848951c6c27fe2735b683dfcb85e47148df Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.durrant@marketdojo.com>
Date: Fri, 14 Jun 2024 11:52:16 +0100
Subject: [PATCH 175/178] test non-function creator

---
 packages/toolkit/src/tests/createSlice.test.ts | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts
index 1569544407..08242224b9 100644
--- a/packages/toolkit/src/tests/createSlice.test.ts
+++ b/packages/toolkit/src/tests/createSlice.test.ts
@@ -1022,9 +1022,7 @@ describe('createSlice', () => {
       test('context.selectSlice throws if unable to find slice state', () => {
         const patchCreator: ReducerCreator<typeof patchCreatorType> = {
           type: patchCreatorType,
-          create() {
-            return { _reducerDefinitionType: patchCreatorType }
-          },
+          create: { _reducerDefinitionType: patchCreatorType },
           handle({ type }, _def, context) {
             const patchedAction = createAction<Patch[]>(type)
             function patchThunk(
@@ -1062,7 +1060,7 @@ describe('createSlice', () => {
           name: 'person',
           initialState: { name: 'Alice' },
           reducers: (create) => ({
-            patchPerson: create.patcher(),
+            patchPerson: create.patcher,
           }),
         })
 
@@ -1244,7 +1242,7 @@ declare module '@reduxjs/toolkit' {
     >
     [patchCreatorType]: ReducerCreatorEntry<
       State extends Objectish
-        ? () => ReducerDefinition<typeof patchCreatorType>
+        ? ReducerDefinition<typeof patchCreatorType>
         : never,
       {
         actions: {

From b6d1d5d1be312b533bd25479e6aa3ff5c8f5f4fa Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.j.durrant@gmail.com>
Date: Sun, 1 Sep 2024 17:49:51 +0100
Subject: [PATCH 176/178] fix type portability examples

---
 .../src/features/polling/pollingSlice.ts      | 22 +++++++------------
 .../src/features/polling/pollingSlice.ts      | 22 +++++++------------
 .../src/features/polling/pollingSlice.ts      | 22 +++++++------------
 3 files changed, 24 insertions(+), 42 deletions(-)

diff --git a/examples/type-portability/bundler/src/features/polling/pollingSlice.ts b/examples/type-portability/bundler/src/features/polling/pollingSlice.ts
index c55eed9266..1ed696c8d3 100644
--- a/examples/type-portability/bundler/src/features/polling/pollingSlice.ts
+++ b/examples/type-portability/bundler/src/features/polling/pollingSlice.ts
@@ -1,4 +1,3 @@
-import type { PayloadAction } from '@reduxjs/toolkit'
 import { createSlice } from '@reduxjs/toolkit'
 import type { RootState } from '../../app/store'
 
@@ -37,27 +36,22 @@ export type PollingAppKey = keyof (typeof initialState)['apps']
 export const pollingSlice = createSlice({
   name: 'polling',
   initialState,
-  reducers: (creators) => {
+  reducers: (create) => {
     return {
-      toggleGlobalPolling: creators.reducer((state) => {
+      toggleGlobalPolling: create.reducer((state) => {
         state.enabled = !state.enabled
       }),
-      updatePolling(
-        state,
-        {
-          payload,
-        }: PayloadAction<{
-          app: PollingAppKey
-          enabled?: boolean
-          interval?: number
-        }>,
-      ) {
+      updatePolling: create.reducer<{
+        app: PollingAppKey
+        enabled?: boolean
+        interval?: number
+      }>((state, { payload }) => {
         const { app, ...rest } = payload
         state.apps[app] = {
           ...state.apps[app],
           ...rest,
         }
-      },
+      }),
     }
   },
   selectors: {
diff --git a/examples/type-portability/nodenext-cjs/src/features/polling/pollingSlice.ts b/examples/type-portability/nodenext-cjs/src/features/polling/pollingSlice.ts
index 24ffcc115c..c70f0b539d 100644
--- a/examples/type-portability/nodenext-cjs/src/features/polling/pollingSlice.ts
+++ b/examples/type-portability/nodenext-cjs/src/features/polling/pollingSlice.ts
@@ -1,6 +1,5 @@
 import ReduxToolkit = require('@reduxjs/toolkit')
 
-import type { PayloadAction } from '@reduxjs/toolkit'
 import type { RootState } from '../../app/store.js'
 
 namespace pollingSliceModule {
@@ -41,27 +40,22 @@ namespace pollingSliceModule {
   export const pollingSlice = createSlice({
     name: 'polling',
     initialState,
-    reducers: (creators) => {
+    reducers: (create) => {
       return {
-        toggleGlobalPolling: creators.reducer((state) => {
+        toggleGlobalPolling: create.reducer((state) => {
           state.enabled = !state.enabled
         }),
-        updatePolling(
-          state,
-          {
-            payload,
-          }: PayloadAction<{
-            app: PollingAppKey
-            enabled?: boolean
-            interval?: number
-          }>,
-        ) {
+        updatePolling: create.reducer<{
+          app: PollingAppKey
+          enabled?: boolean
+          interval?: number
+        }>((state, { payload }) => {
           const { app, ...rest } = payload
           state.apps[app] = {
             ...state.apps[app],
             ...rest,
           }
-        },
+        }),
       }
     },
     selectors: {
diff --git a/examples/type-portability/nodenext-esm/src/features/polling/pollingSlice.ts b/examples/type-portability/nodenext-esm/src/features/polling/pollingSlice.ts
index a1daf6da2a..f9f5e5b8eb 100644
--- a/examples/type-portability/nodenext-esm/src/features/polling/pollingSlice.ts
+++ b/examples/type-portability/nodenext-esm/src/features/polling/pollingSlice.ts
@@ -1,4 +1,3 @@
-import type { PayloadAction } from '@reduxjs/toolkit'
 import { createSlice } from '@reduxjs/toolkit'
 import type { RootState } from '../../app/store.js'
 
@@ -37,27 +36,22 @@ export type PollingAppKey = keyof (typeof initialState)['apps']
 export const pollingSlice = createSlice({
   name: 'polling',
   initialState,
-  reducers: (creators) => {
+  reducers: (create) => {
     return {
-      toggleGlobalPolling: creators.reducer((state) => {
+      toggleGlobalPolling: create.reducer((state) => {
         state.enabled = !state.enabled
       }),
-      updatePolling(
-        state,
-        {
-          payload,
-        }: PayloadAction<{
-          app: PollingAppKey
-          enabled?: boolean
-          interval?: number
-        }>,
-      ) {
+      updatePolling: create.reducer<{
+        app: PollingAppKey
+        enabled?: boolean
+        interval?: number
+      }>((state, { payload }) => {
         const { app, ...rest } = payload
         state.apps[app] = {
           ...state.apps[app],
           ...rest,
         }
-      },
+      }),
     }
   },
   selectors: {

From b10f3454a5fd872bc384f9cce18cf06c3b325c73 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.j.durrant@gmail.com>
Date: Sun, 1 Sep 2024 19:49:27 +0100
Subject: [PATCH 177/178] switch back to an interface

---
 packages/toolkit/src/asyncThunkCreator.ts | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)

diff --git a/packages/toolkit/src/asyncThunkCreator.ts b/packages/toolkit/src/asyncThunkCreator.ts
index d41019cc90..198fb3a1c9 100644
--- a/packages/toolkit/src/asyncThunkCreator.ts
+++ b/packages/toolkit/src/asyncThunkCreator.ts
@@ -83,15 +83,20 @@ export type AsyncThunkSliceReducerConfig<
   options?: AsyncThunkOptions<ThunkArg, ThunkApiConfig>
 }
 
-export type AsyncThunkSliceReducerDefinition<
+export interface AsyncThunkSliceReducerDefinition<
   State,
   ThunkArg extends any,
   Returned = unknown,
   ThunkApiConfig extends AsyncThunkConfig = {},
-> = AsyncThunkSliceReducerConfig<State, ThunkArg, Returned, ThunkApiConfig> &
-  ReducerDefinition<ReducerType.asyncThunk> & {
-    payloadCreator: AsyncThunkPayloadCreator<Returned, ThunkArg, ThunkApiConfig>
-  }
+> extends AsyncThunkSliceReducerConfig<
+      State,
+      ThunkArg,
+      Returned,
+      ThunkApiConfig
+    >,
+    ReducerDefinition<ReducerType.asyncThunk> {
+  payloadCreator: AsyncThunkPayloadCreator<Returned, ThunkArg, ThunkApiConfig>
+}
 
 /**
  * Providing these as part of the config would cause circular types, so we disallow passing them

From ef2d0242b227d949d83fdc7de3cfd06d7a2db247 Mon Sep 17 00:00:00 2001
From: Ben Durrant <ben.durrant@marketdojo.com>
Date: Tue, 3 Sep 2024 10:12:10 +0100
Subject: [PATCH 178/178] avoid relative declaration

---
 packages/toolkit/src/asyncThunkCreator.ts | 64 ++++++++++-------------
 packages/toolkit/src/createSlice.ts       | 15 +++---
 2 files changed, 35 insertions(+), 44 deletions(-)

diff --git a/packages/toolkit/src/asyncThunkCreator.ts b/packages/toolkit/src/asyncThunkCreator.ts
index 198fb3a1c9..a2a974a1bf 100644
--- a/packages/toolkit/src/asyncThunkCreator.ts
+++ b/packages/toolkit/src/asyncThunkCreator.ts
@@ -10,49 +10,39 @@ import type { CaseReducer } from './createReducer'
 import type {
   CreatorCaseReducers,
   ReducerCreator,
-  ReducerCreatorEntry,
   ReducerDefinition,
 } from './createSlice'
 import { ReducerType } from './createSlice'
 import type { Id } from './tsHelpers'
 
-declare module './createSlice' {
-  export interface SliceReducerCreators<
-    State,
-    CaseReducers extends CreatorCaseReducers<State>,
-    Name extends string,
-    ReducerPath extends string,
-  > {
-    [ReducerType.asyncThunk]: ReducerCreatorEntry<
-      AsyncThunkCreator<State>,
-      {
-        actions: {
-          [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition<
-            State,
-            infer ThunkArg,
-            infer Returned,
-            infer ThunkApiConfig
-          >
-            ? AsyncThunk<Returned, ThunkArg, ThunkApiConfig>
-            : never
-        }
-        caseReducers: {
-          [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition<
-            State,
-            any,
-            any,
-            any
-          >
-            ? Id<
-                Pick<
-                  Required<CaseReducers[ReducerName]>,
-                  'fulfilled' | 'rejected' | 'pending' | 'settled'
-                >
-              >
-            : never
-        }
-      }
+export type AsyncThunkCreatorExposes<
+  State,
+  CaseReducers extends CreatorCaseReducers<State>,
+> = {
+  actions: {
+    [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition<
+      State,
+      infer ThunkArg,
+      infer Returned,
+      infer ThunkApiConfig
     >
+      ? AsyncThunk<Returned, ThunkArg, ThunkApiConfig>
+      : never
+  }
+  caseReducers: {
+    [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition<
+      State,
+      any,
+      any,
+      any
+    >
+      ? Id<
+          Pick<
+            Required<CaseReducers[ReducerName]>,
+            'fulfilled' | 'rejected' | 'pending' | 'settled'
+          >
+        >
+      : never
   }
 }
 
diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts
index 54d5a0ac3d..76dfeeb730 100644
--- a/packages/toolkit/src/createSlice.ts
+++ b/packages/toolkit/src/createSlice.ts
@@ -1,5 +1,9 @@
 import type { Action, Reducer, UnknownAction } from 'redux'
 import type { Selector } from 'reselect'
+import type {
+  AsyncThunkCreator,
+  AsyncThunkCreatorExposes,
+} from './asyncThunkCreator'
 import type { InjectConfig } from './combineSlices'
 import type {
   ActionCreatorWithoutPayload,
@@ -9,13 +13,6 @@ import type {
   _ActionCreatorWithPreparedPayload,
 } from './createAction'
 import { createAction } from './createAction'
-import type {
-  AsyncThunk,
-  AsyncThunkConfig,
-  AsyncThunkOptions,
-  AsyncThunkPayloadCreator,
-  OverrideThunkApiConfigs,
-} from './createAsyncThunk'
 import { createAsyncThunk as _createAsyncThunk } from './createAsyncThunk'
 import type {
   ActionMatcherDescriptionCollection,
@@ -145,6 +142,10 @@ export interface SliceReducerCreators<
       }
     }
   >
+  [ReducerType.asyncThunk]: ReducerCreatorEntry<
+    AsyncThunkCreator<State>,
+    AsyncThunkCreatorExposes<State, CaseReducers>
+  >
 }
 
 export type ReducerCreators<