Skip to content

Commit 2eb1513

Browse files
author
ben.durrant
committed
export ReducerCreators, to allow wrapping callback form
1 parent 2bfe54b commit 2eb1513

File tree

3 files changed

+62
-1
lines changed

3 files changed

+62
-1
lines changed

packages/toolkit/src/createSlice.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ interface AsyncThunkCreator<
363363
>
364364
}
365365

366-
interface ReducerCreators<State> {
366+
export interface ReducerCreators<State> {
367367
reducer<Payload>(
368368
caseReducer: CaseReducer<State, PayloadAction<Payload>>
369369
): CaseReducerDefinition<State, PayloadAction<Payload>>

packages/toolkit/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ export type {
7070
ValidateSliceCaseReducers,
7171
CaseReducerWithPrepare,
7272
SliceActionCreator,
73+
ReducerCreators,
7374
} from './createSlice'
7475
export {
7576
// js

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

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import type {
1010
CaseReducer,
1111
PayloadAction,
1212
PayloadActionCreator,
13+
ReducerCreators,
1314
SerializedError,
1415
SliceCaseReducers,
1516
ThunkDispatch,
@@ -18,6 +19,7 @@ import type {
1819
import { configureStore } from '@reduxjs/toolkit'
1920
import { createAction, createSlice } from '@reduxjs/toolkit'
2021
import { expectExactType, expectType, expectUnknown } from './helpers'
22+
import { castDraft } from 'immer'
2123

2224
/*
2325
* Test: Slice name is strongly typed.
@@ -559,6 +561,10 @@ const value = actionCreators.anyKey
559561
expectType<string>(nestedSelectors.selectToFixed(nestedState))
560562
}
561563

564+
/**
565+
* Test: reducer callback
566+
*/
567+
562568
{
563569
interface TestState {
564570
foo: string
@@ -727,3 +733,57 @@ const value = actionCreators.anyKey
727733
)
728734
}
729735
}
736+
737+
/** Test: wrapping createSlice should be possible, with callback */
738+
{
739+
interface GenericState<T> {
740+
data?: T
741+
status: 'loading' | 'finished' | 'error'
742+
}
743+
744+
const createGenericSlice = <
745+
T,
746+
Reducers extends SliceCaseReducers<GenericState<T>>
747+
>({
748+
name = '',
749+
initialState,
750+
reducers,
751+
}: {
752+
name: string
753+
initialState: GenericState<T>
754+
reducers: (create: ReducerCreators<GenericState<T>>) => Reducers
755+
}) => {
756+
return createSlice({
757+
name,
758+
initialState,
759+
reducers: (create) => ({
760+
start: create.reducer((state) => {
761+
state.status = 'loading'
762+
}),
763+
success: create.reducer((state, action: PayloadAction<T>) => {
764+
state.data = castDraft(action.payload)
765+
state.status = 'finished'
766+
}),
767+
...reducers(create),
768+
}),
769+
})
770+
}
771+
772+
const wrappedSlice = createGenericSlice({
773+
name: 'test',
774+
initialState: { status: 'loading' } as GenericState<string>,
775+
reducers: (create) => ({
776+
magic: create.reducer((state) => {
777+
expectType<GenericState<string>>(state)
778+
// @ts-expect-error
779+
expectType<GenericState<number>>(state)
780+
781+
state.status = 'finished'
782+
state.data = 'hocus pocus'
783+
}),
784+
}),
785+
})
786+
787+
expectType<ActionCreatorWithPayload<string>>(wrappedSlice.actions.success)
788+
expectType<ActionCreatorWithoutPayload<string>>(wrappedSlice.actions.magic)
789+
}

0 commit comments

Comments
 (0)