From a4ba0cec84b7fdef9bcabc58ad193f05680bc497 Mon Sep 17 00:00:00 2001 From: Ben Durrant Date: Mon, 15 Apr 2024 17:28:44 +0100 Subject: [PATCH 01/10] Experiment with allowing providing custom slice creators when calling createSlice --- errors.json | 5 +- packages/toolkit/src/createSlice.ts | 86 ++++++++++++++++--- .../toolkit/src/tests/createSlice.test.ts | 23 ++++- 3 files changed, 96 insertions(+), 18 deletions(-) diff --git a/errors.json b/errors.json index 4bdd52e2b6..c3d1e22da0 100644 --- a/errors.json +++ b/errors.json @@ -46,5 +46,6 @@ "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: \"\"" -} + "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: \"\"", + "48": "A creator with the name has already been provided to buildCreateSlice" +} \ No newline at end of file diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts index d359e8ebc9..123dd3d5d7 100644 --- a/packages/toolkit/src/createSlice.ts +++ b/packages/toolkit/src/createSlice.ts @@ -208,6 +208,14 @@ interface InternalReducerHandlingContext { sliceCaseReducersByName: Record actionCreators: Record + + sliceCreators: Record['create']> + sliceCreatorHandlers: Partial< + Record< + RegisteredReducerType, + ReducerCreator['handle'] + > + > } export interface ReducerHandlingContext { @@ -530,12 +538,13 @@ export interface CreateSliceOptions< State, Name, ReducerPath, - CreatorMap + CreatorMap & SliceCreatorMap > = SliceCaseReducers, Name extends string = string, ReducerPath extends string = Name, Selectors extends SliceSelectors = SliceSelectors, CreatorMap extends Record = {}, + SliceCreatorMap extends Record = {}, > { /** * The slice's name. Used to namespace the generated action types. @@ -607,6 +616,10 @@ createSlice({ * A map of selectors that receive the slice's state and any additional arguments, and return a result. */ selectors?: Selectors + + creators?: CreatorOption & { + [K in keyof CreatorMap]?: never + } } export interface CaseReducerDefinition< @@ -837,14 +850,18 @@ const isCreatorCallback = ( ): reducers is CreatorCallback => typeof reducers === 'function' +type CreatorOption> = { + [Name in keyof CreatorMap]: Name extends 'reducer' | 'preparedReducer' + ? never + : ReducerCreator +} & { + asyncThunk?: ReducerCreator +} + interface BuildCreateSliceConfig< CreatorMap extends Record, > { - creators?: { - [Name in keyof CreatorMap]: Name extends 'reducer' | 'preparedReducer' - ? never - : ReducerCreator - } & { asyncThunk?: ReducerCreator } + creators?: CreatorOption } export function buildCreateSlice< @@ -901,10 +918,11 @@ export function buildCreateSlice< State, CaseReducers extends | SliceCaseReducers - | CreatorCallback, + | CreatorCallback, Name extends string, Selectors extends SliceSelectors, ReducerPath extends string = Name, + SliceCreatorMap extends Record = {}, >( options: CreateSliceOptions< State, @@ -912,16 +930,30 @@ export function buildCreateSlice< Name, ReducerPath, Selectors, - CreatorMap + CreatorMap, + SliceCreatorMap >, ): Slice< State, - GetCaseReducers, + GetCaseReducers< + State, + Name, + ReducerPath, + CreatorMap & SliceCreatorMap, + CaseReducers + >, Name, ReducerPath, Selectors > { - const { name, reducerPath = name as unknown as ReducerPath } = options + const { + name, + reducerPath = name as unknown as ReducerPath, + creators: sliceCreators = {} as Record< + string, + ReducerCreator + >, + } = options if (!name) { throw new Error('`name` is a required option for createSlice') } @@ -944,6 +976,20 @@ export function buildCreateSlice< sliceCaseReducersByType: {}, actionCreators: {}, sliceMatchers: [], + sliceCreators: { ...creators }, + sliceCreatorHandlers: { ...handlers }, + } + + for (const [name, creator] of Object.entries(sliceCreators)) { + if (name in creators) { + throw new Error( + `A creator with the name ${name} has already been provided to buildCreateSlice`, + ) + } + internalContext.sliceCreators[name] = creator.create + if ('handle' in creator) { + internalContext.sliceCreatorHandlers[creator.type] = creator.handle + } } function getContext({ reducerName }: ReducerDetails) { @@ -1009,7 +1055,7 @@ export function buildCreateSlice< } if (isCreatorCallback(options.reducers)) { - const reducers = options.reducers(creators as any) + const reducers = options.reducers(internalContext.sliceCreators as any) for (const [reducerName, reducerDefinition] of Object.entries(reducers)) { const { _reducerDefinitionType: type } = reducerDefinition if (typeof type === 'undefined') { @@ -1017,7 +1063,7 @@ export function buildCreateSlice< 'Please use reducer creators passed to callback. Each reducer definition must have a `_reducerDefinitionType` property indicating which handler to use.', ) } - const handle = handlers[type] + const handle = internalContext.sliceCreatorHandlers[type] if (!handle) { throw new Error(`Unsupported reducer type: ${String(type)}`) } @@ -1117,7 +1163,13 @@ export function buildCreateSlice< ): Pick< Slice< State, - GetCaseReducers, + GetCaseReducers< + State, + Name, + ReducerPath, + CreatorMap & SliceCreatorMap, + CaseReducers + >, Name, CurrentReducerPath, Selectors @@ -1173,7 +1225,13 @@ export function buildCreateSlice< const slice: Slice< State, - GetCaseReducers, + GetCaseReducers< + State, + Name, + ReducerPath, + CreatorMap & SliceCreatorMap, + CaseReducers + >, Name, ReducerPath, Selectors diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts index 86c7d5c14b..34727643e7 100644 --- a/packages/toolkit/src/tests/createSlice.test.ts +++ b/packages/toolkit/src/tests/createSlice.test.ts @@ -12,7 +12,6 @@ import type { ReducerCreatorEntry, ReducerCreators, ReducerDefinition, - ReducerDetails, ReducerHandlingContext, SliceActionType, ThunkAction, @@ -36,6 +35,7 @@ import { mockConsole, } from 'console-testing-library/pure' import type { IfMaybeUndefined, NoInfer } from '../tsHelpers' +import { delay } from 'msw' enablePatches() type CreateSlice = typeof createSlice @@ -746,7 +746,9 @@ describe('createSlice', () => { addLoader: loaderCreator.create({}), }), }), - ).toThrowErrorMatchingInlineSnapshot(`[Error: Unsupported reducer type: Symbol(loaderCreatorType)]`) + ).toThrowErrorMatchingInlineSnapshot( + `[Error: Unsupported reducer type: Symbol(loaderCreatorType)]`, + ) const createAppSlice = buildCreateSlice({ creators: { loader: loaderCreator }, }) @@ -1101,6 +1103,23 @@ describe('createSlice', () => { ) }) }) + test('creators can be provided per createSlice call', () => { + const loaderSlice = createSlice({ + name: 'loader', + initialState: {} as Partial>, + creators: { loader: loaderCreator }, + reducers: (create) => ({ + addLoader: create.loader({}), + }), + }) + expect(loaderSlice.actions.addLoader).toEqual(expect.any(Function)) + expect(loaderSlice.actions.addLoader.started).toEqual( + expect.any(Function), + ) + expect(loaderSlice.actions.addLoader.started.type).toBe( + 'loader/addLoader/started', + ) + }) }) }) From e9e781835370a4ee107b0583d75ee9e91245036a Mon Sep 17 00:00:00 2001 From: Ben Durrant Date: Mon, 15 Apr 2024 17:31:05 +0100 Subject: [PATCH 02/10] rm unused import --- 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 34727643e7..7c46807f63 100644 --- a/packages/toolkit/src/tests/createSlice.test.ts +++ b/packages/toolkit/src/tests/createSlice.test.ts @@ -35,7 +35,7 @@ import { mockConsole, } from 'console-testing-library/pure' import type { IfMaybeUndefined, NoInfer } from '../tsHelpers' -import { delay } from 'msw' + enablePatches() type CreateSlice = typeof createSlice From 108f6b30d52a15322efbfb55c644f888ec08dba3 Mon Sep 17 00:00:00 2001 From: Ben Durrant Date: Mon, 15 Apr 2024 17:35:07 +0100 Subject: [PATCH 03/10] add type test for overlapping names --- .../toolkit/src/tests/createSlice.test-d.ts | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/packages/toolkit/src/tests/createSlice.test-d.ts b/packages/toolkit/src/tests/createSlice.test-d.ts index ea0308ce31..1f216cd599 100644 --- a/packages/toolkit/src/tests/createSlice.test-d.ts +++ b/packages/toolkit/src/tests/createSlice.test-d.ts @@ -856,6 +856,37 @@ describe('type tests', () => { }, }) }) + test('creators can be provided during createSlice call but cannot overlap', () => { + const createAppSlice = buildCreateSlice({ + creators: { asyncThunk: asyncThunkCreator }, + }) + + createAppSlice({ + name: 'counter', + initialState: 0, + creators: { + something: asyncThunkCreator, + }, + reducers: (create) => { + expectTypeOf(create).toHaveProperty('asyncThunk') + expectTypeOf(create).toHaveProperty('something') + return {} + }, + }) + + createAppSlice({ + name: 'counter', + initialState: 0, + // @ts-expect-error + creators: { + asyncThunk: asyncThunkCreator, + }, + reducers: (create) => { + expectTypeOf(create).toHaveProperty('asyncThunk') + return {} + }, + }) + }) }) interface Toast { From 42363fa86f6abddb76416bb4c7e6b1a9f29161cf Mon Sep 17 00:00:00 2001 From: Ben Durrant Date: Sat, 4 May 2024 13:26:15 +0100 Subject: [PATCH 04/10] add overlap test --- .../toolkit/src/tests/createSlice.test.ts | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/packages/toolkit/src/tests/createSlice.test.ts b/packages/toolkit/src/tests/createSlice.test.ts index 7c46807f63..2a1d89339b 100644 --- a/packages/toolkit/src/tests/createSlice.test.ts +++ b/packages/toolkit/src/tests/createSlice.test.ts @@ -1120,6 +1120,26 @@ describe('createSlice', () => { 'loader/addLoader/started', ) }) + test('error is thrown if there is name overlap between creators', () => { + const createAppSlice = buildCreateSlice({ + creators: { + loader: loaderCreator, + }, + }) + expect(() => + createAppSlice({ + name: 'loader', + initialState: {} as Partial>, + // @ts-expect-error name overlap + creators: { loader: loaderCreator }, + reducers: (create) => ({ + addLoader: create.loader({}), + }), + }), + ).toThrowErrorMatchingInlineSnapshot( + `[Error: A creator with the name loader has already been provided to buildCreateSlice]`, + ) + }) }) }) From d976ea501ff180228497cd0a0c1939c936f9483a Mon Sep 17 00:00:00 2001 From: Ben Durrant Date: Wed, 22 May 2024 11:13:47 +0100 Subject: [PATCH 05/10] 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, + Name extends string, + ReducerPath extends string, + > { + [ReducerType.asyncThunk]: ReducerCreatorEntry< + AsyncThunkCreator, + { + actions: { + [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition< + State, + infer ThunkArg, + infer Returned, + infer ThunkApiConfig + > + ? AsyncThunk + : never + } + caseReducers: { + [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition< + State, + any, + any, + any + > + ? Id< + Pick< + Required, + 'fulfilled' | 'rejected' | 'pending' | 'settled' + > + > + : never + } + } + > + } +} export interface AsyncThunkSliceReducerConfig< State, diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts index 481a6d36d4..1b3a099118 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, - { - actions: { - [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition< - State, - infer ThunkArg, - infer Returned, - infer ThunkApiConfig - > - ? AsyncThunk - : never - } - caseReducers: { - [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition< - State, - any, - any, - any - > - ? Id< - Pick< - Required, - 'fulfilled' | 'rejected' | 'pending' | 'settled' - > - > - : never - } - } - > } export type ReducerCreators< From 71e3d04e2825a31671e7199c0084b716e8f95927 Mon Sep 17 00:00:00 2001 From: Ben Durrant Date: Wed, 22 May 2024 14:50:11 +0100 Subject: [PATCH 06/10] create util to cut down on repetitive code --- .../toolkit/src/entities/slice_creator.ts | 83 ++++++++----------- 1 file changed, 33 insertions(+), 50 deletions(-) diff --git a/packages/toolkit/src/entities/slice_creator.ts b/packages/toolkit/src/entities/slice_creator.ts index 77d2456a21..c4097c0023 100644 --- a/packages/toolkit/src/entities/slice_creator.ts +++ b/packages/toolkit/src/entities/slice_creator.ts @@ -1,12 +1,13 @@ import type { + CaseReducerDefinition, CreatorCaseReducers, + PayloadAction, ReducerCreator, ReducerCreatorEntry, } from '@reduxjs/toolkit' import { reducerCreator } from '../createSlice' import type { WithRequiredProp } from '../tsHelpers' import type { - Update, EntityAdapter, EntityId, EntityState, @@ -79,6 +80,20 @@ declare module '@reduxjs/toolkit' { } } +const makeWrappedReducerCreator = + ( + selectEntityState: (state: State) => EntityState, + ) => + ( + reducer: ( + state: EntityState, + action: PayloadAction, + ) => void, + ): CaseReducerDefinition> => + reducerCreator.create((state: State, action) => + reducer(selectEntityState(state), action), + ) + export function createEntityMethods< T, Id extends EntityId, @@ -98,58 +113,26 @@ export function createEntityMethods< const name = nameParam as 's' const pluralName = pluralParam as 'p' - const reducer = reducerCreator.create + const reducer = makeWrappedReducerCreator(selectEntityState) const reducers: EntityReducers = { - [`addOne${capitalize(name)}` as const]: reducer((state, action) => { - adapter.addOne(selectEntityState(state), action) - }), - [`addMany${capitalize(pluralName)}` as const]: reducer< - readonly T[] | Record - >((state, action) => { - adapter.addMany(selectEntityState(state), action) - }), - [`setOne${capitalize(name)}` as const]: reducer((state, action) => { - adapter.setOne(selectEntityState(state), action) - }), - [`setMany${capitalize(pluralName)}` as const]: reducer< - readonly T[] | Record - >((state, action) => { - adapter.setMany(selectEntityState(state), action) - }), - [`setAll${capitalize(pluralName)}` as const]: reducer< - readonly T[] | Record - >((state, action) => { - adapter.setAll(selectEntityState(state), action) - }), - [`removeOne${capitalize(name)}` as const]: reducer((state, action) => { - adapter.removeOne(selectEntityState(state), action) - }), - [`removeMany${capitalize(pluralName)}` as const]: reducer( - (state, action) => { - adapter.removeMany(selectEntityState(state), action) - }, + [`addOne${capitalize(name)}` as const]: reducer(adapter.addOne), + [`addMany${capitalize(pluralName)}` as const]: reducer(adapter.addMany), + [`setOne${capitalize(name)}` as const]: reducer(adapter.setOne), + [`setMany${capitalize(pluralName)}` as const]: reducer(adapter.setMany), + [`setAll${capitalize(pluralName)}` as const]: reducer(adapter.setAll), + [`removeOne${capitalize(name)}` as const]: reducer(adapter.removeOne), + [`removeMany${capitalize(pluralName)}` as const]: reducer( + adapter.removeMany, + ), + [`removeAll${capitalize(pluralName)}` as const]: reducer(adapter.removeAll), + [`upsertOne${capitalize(name)}` as const]: reducer(adapter.upsertOne), + [`upsertMany${capitalize(pluralName)}` as const]: reducer( + adapter.upsertMany, ), - [`removeAll${capitalize(pluralName)}` as const]: reducer((state) => { - adapter.removeAll(selectEntityState(state)) - }), - [`upsertOne${capitalize(name)}` as const]: reducer((state, action) => { - adapter.upsertOne(selectEntityState(state), action) - }), - [`upsertMany${capitalize(pluralName)}` as const]: reducer< - readonly T[] | Record - >((state, action) => { - adapter.upsertMany(selectEntityState(state), action) - }), - [`updateOne${capitalize(name)}` as const]: reducer>( - (state, action) => { - adapter.updateOne(selectEntityState(state), action) - }, + [`updateOne${capitalize(name)}` as const]: reducer(adapter.updateOne), + [`updateMany${capitalize(pluralName)}` as const]: reducer( + adapter.updateMany, ), - [`updateMany${capitalize(pluralName)}` as const]: reducer< - readonly Update[] - >((state, action) => { - adapter.updateMany(selectEntityState(state), action) - }), } return reducers as any } From fea70e40d8ad794c4fe00e66ed23145a4ae4b289 Mon Sep 17 00:00:00 2001 From: Ben Durrant Date: Wed, 29 May 2024 17:49:34 +0100 Subject: [PATCH 07/10] update docs --- docs/api/createSlice.mdx | 14 ++++++++++++-- docs/usage/custom-slice-creators.mdx | 10 ++++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/docs/api/createSlice.mdx b/docs/api/createSlice.mdx index bd3afde05b..92896432d2 100644 --- a/docs/api/createSlice.mdx +++ b/docs/api/createSlice.mdx @@ -57,14 +57,16 @@ function createSlice({ name: string, // The initial state for the reducer initialState: State, - // An object of "case reducers". Key names will be used to generate actions. - reducers: Record, + // An object of "case reducers", or a callback that returns an object. Key names will be used to generate actions. + reducers: Record | ((create: ReducerCreators) => Record), // A "builder callback" function used to add more reducers extraReducers?: (builder: ActionReducerMapBuilder) => void, // A preference for the slice reducer's location, used by `combineSlices` and `slice.selectors`. Defaults to `name`. reducerPath?: string, // An object of selectors, which receive the slice's state as their first parameter. selectors?: Record any>, + // An object of custom slice creators, used by the reducer callback. + creators?: Record }) ``` @@ -456,6 +458,14 @@ const counterSlice = createSlice({ ::: +### `creators` + +While typically [custom creators](/usage/custom-slice-creators) will be provided on a per-app basis (see [`buildCreateSlice`](#buildcreateslice)), this field allows for custom slice creators to be passed in per slice. + +This is particularly useful when using a custom creator that is specific to a single slice. + +An error will be thrown if there is a naming conflict between an app-wide custom creator and a slice-specific custom creator. + ## Return Value `createSlice` will return an object that looks like: diff --git a/docs/usage/custom-slice-creators.mdx b/docs/usage/custom-slice-creators.mdx index eb5ae25f7d..1b977f892f 100644 --- a/docs/usage/custom-slice-creators.mdx +++ b/docs/usage/custom-slice-creators.mdx @@ -48,6 +48,12 @@ const { undo, redo, reset, updateTitle, togglePinned } = 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). +:::note + +Creators can also be [passed per slice](/api/createSlice#creators), but most creators will be useful in more than one slice - so it's recommended to pass them to `buildCreateSlice` instead. + +::: + ```ts title="Creator callback for reducers" import { buildCreateSlice, asyncThunkCreator, nanoid } from '@reduxjs/toolkit' @@ -166,7 +172,7 @@ The [creator definition](#creator-definitions) for `create.preparedReducer` is e 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`: +The name the creator is available under is based on the key used when calling `buildCreateSlice` (or [`createSlice`](/api/createSlice#creators)). For example, to use `create.asyncThunk`: ```ts import { buildCreateSlice, asyncThunkCreator } from '@reduxjs/toolkit' @@ -464,7 +470,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`](../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. +It's passed to [`buildCreateSlice`](../api/createSlice#buildcreateslice) (or [`createSlice`](/api/createSlice#creators)) 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' From ed623ac9bdb1763ae73dc4403a1caf593fee6525 Mon Sep 17 00:00:00 2001 From: Ben Durrant Date: Tue, 3 Sep 2024 10:28:55 +0100 Subject: [PATCH 08/10] fix creator issue --- packages/toolkit/src/asyncThunkCreator.ts | 55 +++++++++---------- packages/toolkit/src/createSlice.ts | 18 ++++-- .../toolkit/src/entities/slice_creator.ts | 4 -- 3 files changed, 39 insertions(+), 38 deletions(-) diff --git a/packages/toolkit/src/asyncThunkCreator.ts b/packages/toolkit/src/asyncThunkCreator.ts index b7e9ec7cb7..cf797d10b8 100644 --- a/packages/toolkit/src/asyncThunkCreator.ts +++ b/packages/toolkit/src/asyncThunkCreator.ts @@ -115,40 +115,35 @@ export interface AsyncThunkCreator< > } -export type AsyncThunkCreators< +export type AsyncThunkCreatorExposes< State, CaseReducers extends CreatorCaseReducers, > = { - [ReducerType.asyncThunk]: ReducerCreatorEntry< - AsyncThunkCreator, - { - actions: { - [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition< - State, - infer ThunkArg, - infer Returned, - infer ThunkApiConfig - > - ? AsyncThunk - : never - } - caseReducers: { - [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition< - State, - any, - any, - any + actions: { + [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition< + State, + infer ThunkArg, + infer Returned, + infer ThunkApiConfig + > + ? AsyncThunk + : never + } + caseReducers: { + [ReducerName in keyof CaseReducers]: CaseReducers[ReducerName] extends AsyncThunkSliceReducerDefinition< + State, + any, + any, + any + > + ? Id< + Pick< + Required, + 'fulfilled' | 'rejected' | 'pending' | 'settled' + > > - ? Id< - Pick< - Required, - 'fulfilled' | 'rejected' | 'pending' | 'settled' - > - > - : never - } - } - > + : never + } } export const asyncThunkCreator: ReducerCreator = { diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts index dcf3ee98a3..5bf5c9744c 100644 --- a/packages/toolkit/src/createSlice.ts +++ b/packages/toolkit/src/createSlice.ts @@ -1,6 +1,9 @@ import type { Action, Reducer, UnknownAction } from 'redux' import type { Selector } from 'reselect' -import type { AsyncThunkCreators } from './asyncThunkCreator' +import type { + AsyncThunkCreator, + AsyncThunkCreatorExposes, +} from './asyncThunkCreator' import type { InjectConfig } from './combineSlices' import type { ActionCreatorWithoutPayload, @@ -17,7 +20,10 @@ import type { ReducerWithInitialState, } from './createReducer' import { createReducer, makeGetInitialState } from './createReducer' -import type { EntityCreators } from './entities/slice_creator' +import type { + EntityMethodsCreator, + entityMethodsCreatorType, +} from './entities/slice_creator' import type { ActionReducerMapBuilder, TypedActionCreator } from './mapBuilders' import { executeReducerBuilderCallback } from './mapBuilders' import type { @@ -74,8 +80,7 @@ export interface SliceReducerCreators< CaseReducers extends CreatorCaseReducers, Name extends string, ReducerPath extends string, -> extends AsyncThunkCreators, - EntityCreators { +> { [ReducerType.reducer]: ReducerCreatorEntry< { ( @@ -141,6 +146,11 @@ export interface SliceReducerCreators< } } > + [ReducerType.asyncThunk]: ReducerCreatorEntry< + AsyncThunkCreator, + AsyncThunkCreatorExposes + > + [entityMethodsCreatorType]: ReducerCreatorEntry> } export type ReducerCreators< diff --git a/packages/toolkit/src/entities/slice_creator.ts b/packages/toolkit/src/entities/slice_creator.ts index b1fbe29903..f32c2dc460 100644 --- a/packages/toolkit/src/entities/slice_creator.ts +++ b/packages/toolkit/src/entities/slice_creator.ts @@ -68,10 +68,6 @@ export type EntityMethodsCreator = >, ) => EntityReducers -export type EntityCreators = { - [entityMethodsCreatorType]: ReducerCreatorEntry> -} - const makeWrappedReducerCreator = ( selectEntityState: (state: State) => EntityState, From e4dbc9a1f103aef0efe804d4b26bf6d74a153aed Mon Sep 17 00:00:00 2001 From: Ben Durrant Date: Fri, 25 Oct 2024 10:28:16 +0100 Subject: [PATCH 09/10] Retry CSB build From 5d1969d1645be73721e734e6ecd647d8db1583d5 Mon Sep 17 00:00:00 2001 From: Ben Durrant Date: Fri, 25 Oct 2024 10:37:38 +0100 Subject: [PATCH 10/10] fix Id usage --- packages/toolkit/src/entities/models.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/toolkit/src/entities/models.ts b/packages/toolkit/src/entities/models.ts index 346bad0e65..2b20b84826 100644 --- a/packages/toolkit/src/entities/models.ts +++ b/packages/toolkit/src/entities/models.ts @@ -166,7 +166,7 @@ export type EntitySelectors< IdType extends EntityId, Single extends string = '', Plural extends string = DefaultPlural, -> = Compute< +> = Id< { [K in `select${Capitalize}Ids`]: (state: V) => IdType[] } & { @@ -181,7 +181,7 @@ export type EntitySelectors< [K in `select${Capitalize}ById`]: ( state: V, id: IdType, - ) => Compute> + ) => Id> } >