Skip to content

Commit 7dcab4e

Browse files
authored
createSlice: use template literal types for action type (#2250)
1 parent 8ec8c39 commit 7dcab4e

File tree

2 files changed

+36
-17
lines changed

2 files changed

+36
-17
lines changed

packages/toolkit/src/createSlice.ts

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export interface Slice<
5252
* Action creators for the types of actions that are handled by the slice
5353
* reducer.
5454
*/
55-
actions: CaseReducerActions<CaseReducers>
55+
actions: CaseReducerActions<CaseReducers, Name>
5656

5757
/**
5858
* The individual case reducer functions that were passed in the `reducers` parameter.
@@ -165,38 +165,54 @@ export type SliceCaseReducers<State> = {
165165
| CaseReducerWithPrepare<State, PayloadAction<any, string, any, any>>
166166
}
167167

168+
type SliceActionType<
169+
SliceName extends string,
170+
ActionName extends keyof any
171+
> = ActionName extends string | number ? `${SliceName}/${ActionName}` : string
172+
168173
/**
169174
* Derives the slice's `actions` property from the `reducers` options
170175
*
171176
* @public
172177
*/
173-
export type CaseReducerActions<CaseReducers extends SliceCaseReducers<any>> = {
178+
export type CaseReducerActions<
179+
CaseReducers extends SliceCaseReducers<any>,
180+
SliceName extends string
181+
> = {
174182
[Type in keyof CaseReducers]: CaseReducers[Type] extends { prepare: any }
175-
? ActionCreatorForCaseReducerWithPrepare<CaseReducers[Type]>
176-
: ActionCreatorForCaseReducer<CaseReducers[Type]>
183+
? ActionCreatorForCaseReducerWithPrepare<
184+
CaseReducers[Type],
185+
SliceActionType<SliceName, Type>
186+
>
187+
: ActionCreatorForCaseReducer<
188+
CaseReducers[Type],
189+
SliceActionType<SliceName, Type>
190+
>
177191
}
178192

179193
/**
180194
* Get a `PayloadActionCreator` type for a passed `CaseReducerWithPrepare`
181195
*
182196
* @internal
183197
*/
184-
type ActionCreatorForCaseReducerWithPrepare<CR extends { prepare: any }> =
185-
_ActionCreatorWithPreparedPayload<CR['prepare'], string>
198+
type ActionCreatorForCaseReducerWithPrepare<
199+
CR extends { prepare: any },
200+
Type extends string
201+
> = _ActionCreatorWithPreparedPayload<CR['prepare'], Type>
186202

187203
/**
188204
* Get a `PayloadActionCreator` type for a passed `CaseReducer`
189205
*
190206
* @internal
191207
*/
192-
type ActionCreatorForCaseReducer<CR> = CR extends (
208+
type ActionCreatorForCaseReducer<CR, Type extends string> = CR extends (
193209
state: any,
194210
action: infer Action
195211
) => any
196212
? Action extends { payload: infer P }
197-
? PayloadActionCreator<P>
198-
: ActionCreatorWithoutPayload
199-
: ActionCreatorWithoutPayload
213+
? PayloadActionCreator<P, Type>
214+
: ActionCreatorWithoutPayload<Type>
215+
: ActionCreatorWithoutPayload<Type>
200216

201217
/**
202218
* Extracts the CaseReducers out of a `reducers` object, even if they are

packages/toolkit/src/tests/createSlice.typetest.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -154,12 +154,13 @@ const value = actionCreators.anyKey
154154
},
155155
})
156156

157-
const s: string = counter.actions.increment.type
158-
const t: string = counter.actions.decrement.type
159-
const u: string = counter.actions.multiply.type
157+
const s: 'counter/increment' = counter.actions.increment.type
158+
const sa: 'counter/increment' = counter.actions.increment().type
159+
const t: 'counter/decrement' = counter.actions.decrement.type
160+
const ta: 'counter/decrement' = counter.actions.decrement().type
161+
const u: 'counter/multiply' = counter.actions.multiply.type
162+
const ua: 'counter/multiply' = counter.actions.multiply(1).type
160163

161-
// @ts-expect-error
162-
const x: 'counter/increment' = counter.actions.increment.type
163164
// @ts-expect-error
164165
const y: 'increment' = counter.actions.increment.type
165166
}
@@ -192,7 +193,9 @@ const value = actionCreators.anyKey
192193
},
193194
})
194195

195-
expectType<string>(counter.actions.incrementByStrLen('test').type)
196+
expectType<'test/incrementByStrLen'>(
197+
counter.actions.incrementByStrLen('test').type
198+
)
196199
expectType<number>(counter.actions.incrementByStrLen('test').payload)
197200
expectType<string>(counter.actions.concatMetaStrLen('test').payload)
198201
expectType<number>(counter.actions.concatMetaStrLen('test').meta)
@@ -384,7 +387,7 @@ const value = actionCreators.anyKey
384387

385388
const x: Action<unknown> = {} as any
386389
if (mySlice.actions.setName.match(x)) {
387-
expectType<string>(x.type)
390+
expectType<'name/setName'>(x.type)
388391
expectType<string>(x.payload)
389392
} else {
390393
// @ts-expect-error

0 commit comments

Comments
 (0)