Skip to content

Commit 6b0fb6e

Browse files
committed
createSlice: use template literal types for action type
1 parent 48689d4 commit 6b0fb6e

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
@@ -51,7 +51,7 @@ export interface Slice<
5151
* Action creators for the types of actions that are handled by the slice
5252
* reducer.
5353
*/
54-
actions: CaseReducerActions<CaseReducers>
54+
actions: CaseReducerActions<CaseReducers, Name>
5555

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

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

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

186202
/**
187203
* Get a `PayloadActionCreator` type for a passed `CaseReducer`
188204
*
189205
* @internal
190206
*/
191-
type ActionCreatorForCaseReducer<CR> = CR extends (
207+
type ActionCreatorForCaseReducer<CR, Type extends string> = CR extends (
192208
state: any,
193209
action: infer Action
194210
) => any
195211
? Action extends { payload: infer P }
196-
? PayloadActionCreator<P>
197-
: ActionCreatorWithoutPayload
198-
: ActionCreatorWithoutPayload
212+
? PayloadActionCreator<P, Type>
213+
: ActionCreatorWithoutPayload<Type>
214+
: ActionCreatorWithoutPayload<Type>
199215

200216
/**
201217
* 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)