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}`]: EntityStateAdapter< T, Id - >[K] extends infer Method - ? Method extends CaseReducer - ? CaseReducerDefinition - : Method extends CaseReducer> - ? CaseReducerDefinition> - : never - : never + >[K] extends (state: any) => any + ? CaseReducerDefinition + : EntityStateAdapter[K] extends CaseReducer + ? CaseReducerDefinition + : never } export interface EntityMethodsCreatorConfig< @@ -50,7 +49,7 @@ export interface EntityMethodsCreatorConfig< Single extends string, Plural extends string, > { - selectEntityState?: (state: CastAny) => EntityState + selectEntityState?: (state: State) => EntityState 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, + Single extends string = '', + Plural extends string = DefaultPlural, + >( + adapter: EntityAdapter, { - selectEntityState = (state) => state as EntityState, - // 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 = {}, - ): EntityReducers { + selectEntityState = (state) => state as EntityState, + name: nameParam = '' as Single, + pluralName: pluralParam = (nameParam && `${nameParam}s`) as Plural, + }: EntityMethodsCreatorConfig = {}, + ): EntityReducers { + // 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((state, action) => { + [`addOne${capitalize(name)}` as const]: reducer((state, action) => { adapter.addOne(selectEntityState(state), action.payload) }), - [`addMany${capitalize(pluralName)}` as const]: reducer( - (state, action) => { - adapter.addMany(selectEntityState(state), action.payload) - }, - ), - [`setOne${capitalize(name)}` as const]: reducer((state, action) => { + [`addMany${capitalize(pluralName)}` as const]: reducer< + readonly T[] | Record + >((state, action) => { + adapter.addMany(selectEntityState(state), action.payload) + }), + [`setOne${capitalize(name)}` as const]: reducer((state, action) => { adapter.setOne(selectEntityState(state), action.payload) }), - [`setMany${capitalize(pluralName)}` as const]: reducer( - (state, action) => { - adapter.setMany(selectEntityState(state), action.payload) - }, - ), - [`setAll${capitalize(pluralName)}` as const]: reducer( - (state, action) => { - adapter.setAll(selectEntityState(state), action.payload) - }, - ), - [`removeOne${capitalize(name)}` as const]: reducer( + [`setMany${capitalize(pluralName)}` as const]: reducer< + readonly T[] | Record + >((state, action) => { + adapter.setMany(selectEntityState(state), action.payload) + }), + [`setAll${capitalize(pluralName)}` as const]: reducer< + readonly T[] | Record + >((state, action) => { + adapter.setAll(selectEntityState(state), action.payload) + }), + [`removeOne${capitalize(name)}` as const]: reducer( (state, action) => { adapter.removeOne(selectEntityState(state), action.payload) }, ), - [`removeMany${capitalize(pluralName)}` as const]: reducer( + [`removeMany${capitalize(pluralName)}` as const]: reducer( (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( - (state, action) => { - adapter.upsertOne(selectEntityState(state), action.payload) - }, - ), - [`upsertMany${capitalize(pluralName)}` as const]: reducer( - (state, action) => { - adapter.upsertMany(selectEntityState(state), action.payload) - }, - ), - [`updateOne${capitalize(name)}` as const]: reducer( + [`upsertOne${capitalize(name)}` as const]: reducer((state, action) => { + adapter.upsertOne(selectEntityState(state), action.payload) + }), + [`upsertMany${capitalize(pluralName)}` as const]: reducer< + readonly T[] | Record + >((state, action) => { + adapter.upsertMany(selectEntityState(state), action.payload) + }), + [`updateOne${capitalize(name)}` as const]: reducer>( (state, action) => { adapter.updateOne(selectEntityState(state), action.payload) }, ), - [`updateMany${capitalize(pluralName)}` as const]: reducer( - (state, action) => { - adapter.updateMany(selectEntityState(state), action.payload) - }, - ), - } + [`updateMany${capitalize(pluralName)}` as const]: reducer< + readonly Update[] + >((state, action) => { + adapter.updateMany(selectEntityState(state), action.payload) + }), + } satisfies EntityReducers as any }, -} +} satisfies ReducerCreator