diff --git a/src/configureStore.ts b/src/configureStore.ts
index b2ae29c262..bcc7a92cba 100644
--- a/src/configureStore.ts
+++ b/src/configureStore.ts
@@ -129,7 +129,7 @@ export interface EnhancedStore<
export function configureStore<
S = any,
A extends Action = AnyAction,
- M extends Middlewares = [ThunkMiddlewareFor]
+ M extends Middlewares = [ThunkMiddlewareFor]
>(options: ConfigureStoreOptions): EnhancedStore {
const curriedGetDefaultMiddleware = curryGetDefaultMiddleware()
diff --git a/src/createSlice.ts b/src/createSlice.ts
index 85065c4d68..9811bf00cc 100644
--- a/src/createSlice.ts
+++ b/src/createSlice.ts
@@ -23,6 +23,10 @@ import { NoInfer } from './tsHelpers'
*/
export type SliceActionCreator
= PayloadActionCreator
+export type ActionsOfCaseReducersActions>> = {
+ [Type in keyof T]: ReturnType
+}[keyof T]
+
/**
* The return value of `createSlice`
*
@@ -41,13 +45,13 @@ export interface Slice<
/**
* The slice's reducer.
*/
- reducer: Reducer
+ reducer: Reducer>>
/**
* Action creators for the types of actions that are handled by the slice
* reducer.
*/
- actions: CaseReducerActions
+ actions: CaseReducerActions
/**
* The individual case reducer functions that were passed in the `reducers` parameter.
@@ -159,10 +163,15 @@ export type SliceCaseReducers = {
*
* @public
*/
-export type CaseReducerActions> = {
- [Type in keyof CaseReducers]: CaseReducers[Type] extends { prepare: any }
- ? ActionCreatorForCaseReducerWithPrepare
- : ActionCreatorForCaseReducer
+export type CaseReducerActions<
+ CaseReducers extends SliceCaseReducers,
+ SliceName extends string = string
+> = {
+ [Type in string & keyof CaseReducers]: CaseReducers[Type] extends {
+ prepare: any
+ }
+ ? ActionCreatorForCaseReducerWithPrepare
+ : ActionCreatorForCaseReducer
}
/**
@@ -171,22 +180,23 @@ export type CaseReducerActions> = {
* @internal
*/
type ActionCreatorForCaseReducerWithPrepare<
- CR extends { prepare: any }
-> = _ActionCreatorWithPreparedPayload
+ CR extends { prepare: any },
+ Type extends string = string
+> = _ActionCreatorWithPreparedPayload
/**
* Get a `PayloadActionCreator` type for a passed `CaseReducer`
*
* @internal
*/
-type ActionCreatorForCaseReducer = CR extends (
- state: any,
- action: infer Action
-) => any
+type ActionCreatorForCaseReducer<
+ CR,
+ Type extends string = string
+> = CR extends (state: any, action: infer Action) => any
? Action extends { payload: infer P }
- ? PayloadActionCreator
- : ActionCreatorWithoutPayload
- : ActionCreatorWithoutPayload
+ ? PayloadActionCreator
+ : ActionCreatorWithoutPayload
+ : ActionCreatorWithoutPayload
/**
* Extracts the CaseReducers out of a `reducers` object, even if they are
diff --git a/src/getDefaultMiddleware.ts b/src/getDefaultMiddleware.ts
index 02eb736f10..8b983398aa 100644
--- a/src/getDefaultMiddleware.ts
+++ b/src/getDefaultMiddleware.ts
@@ -29,16 +29,17 @@ interface GetDefaultMiddlewareOptions {
export type ThunkMiddlewareFor<
S,
- O extends GetDefaultMiddlewareOptions = {}
+ O extends GetDefaultMiddlewareOptions = {},
+ A extends AnyAction = AnyAction
> = O extends {
thunk: false
}
? never
: O extends { thunk: { extraArgument: infer E } }
- ? ThunkMiddleware
+ ? ThunkMiddleware
:
- | ThunkMiddleware //The ThunkMiddleware with a `null` ExtraArgument is here to provide backwards-compatibility.
- | ThunkMiddleware
+ | ThunkMiddleware //The ThunkMiddleware with a `null` ExtraArgument is here to provide backwards-compatibility.
+ | ThunkMiddleware
export type CurriedGetDefaultMiddleware = <
O extends Partial = {
diff --git a/type-tests/files/createSlice.typetest.ts b/type-tests/files/createSlice.typetest.ts
index 1039a8a48e..07a0adcfdb 100644
--- a/type-tests/files/createSlice.typetest.ts
+++ b/type-tests/files/createSlice.typetest.ts
@@ -1,4 +1,4 @@
-import { Action, AnyAction, Reducer } from 'redux'
+import { Action, AnyAction, combineReducers, Reducer } from 'redux'
import {
ActionCreatorWithNonInferrablePayload,
ActionCreatorWithOptionalPayload,
@@ -68,8 +68,12 @@ const value = actionCreators.anyKey
/* Reducer */
- const reducer: Reducer = slice.reducer
+ type InferredTypeActions = 'counter/increment' | 'counter/decrement'
+ const reducer: Reducer> = slice.reducer
+
+ // @ts-expect-error
+ const stringTypeActionsReducer: Reducer = slice.reducer
// @ts-expect-error
const stringReducer: Reducer = slice.reducer
// @ts-expect-error
@@ -84,6 +88,40 @@ const value = actionCreators.anyKey
slice.actions.other(1)
}
+/*
+ * Test: createSlice() safetly type actions with combineReducers
+ */
+{
+ const firstAction = createAction<{ count: number }>('FIRST_ACTION')
+
+ const slice = createSlice({
+ name: 'counter',
+ initialState: 0,
+ reducers: {
+ increment: (state: number, action) => state + action.payload,
+ decrement: (state: number, action) => state - action.payload
+ },
+ extraReducers: {
+ [firstAction.type]: (state: number, action) =>
+ state + action.payload.count
+ }
+ })
+
+ /* Reducer */
+
+ const reducer = combineReducers({
+ counter: slice.reducer,
+ })
+
+ reducer({ counter: 0 }, { type: 'counter/decrement', payload: undefined })
+ reducer({ counter: 0 }, { type: 'counter/increment', payload: undefined })
+
+ // @ts-expect-error
+ reducer({ counter: 0 }, { type: 'decrement', payload: undefined })
+ // @ts-expect-error
+ reducer({ counter: 0 }, { type: 'any-string', payload: undefined })
+}
+
/*
* Test: Slice action creator types are inferred.
*/
@@ -139,7 +177,7 @@ const value = actionCreators.anyKey
}
/*
- * Test: Slice action creator types properties are "string"
+ * Test: Slice action creator types properties are `${sliceName}/${reducerPropertyName}`
*/
{
const counter = createSlice({
@@ -159,10 +197,16 @@ const value = actionCreators.anyKey
const t: string = counter.actions.decrement.type
const u: string = counter.actions.multiply.type
+ const a: 'counter/increment' = counter.actions.increment.type
+ const b: 'counter/decrement' = counter.actions.decrement.type
+ const c: 'counter/multiply' = counter.actions.multiply.type
+
+ // @ts-expect-error
+ const x: 'increment' = counter.actions.increment.type
// @ts-expect-error
- const x: 'counter/increment' = counter.actions.increment.type
+ const y: 'decrement' = counter.actions.decrement.type
// @ts-expect-error
- const y: 'increment' = counter.actions.increment.type
+ const z: 'multiply' = counter.actions.multiply.type
}
/*
@@ -193,7 +237,7 @@ const value = actionCreators.anyKey
}
})
- expectType(counter.actions.incrementByStrLen('test').type)
+ expectType<'test/incrementByStrLen'>(counter.actions.incrementByStrLen('test').type)
expectType(counter.actions.incrementByStrLen('test').payload)
expectType(counter.actions.concatMetaStrLen('test').payload)
expectType(counter.actions.concatMetaStrLen('test').meta)
@@ -272,7 +316,7 @@ const value = actionCreators.anyKey
name: 'counter',
initialState: 0,
reducers: {
- increment(state, action: PayloadAction) {
+ increment(state, action) {
return state + action.payload
},
decrement: {