@@ -18,7 +18,9 @@ export type SliceActionCreator<P> = PayloadActionCreator<P>
18
18
19
19
export interface Slice <
20
20
State = any ,
21
- ActionCreators extends { [ key : string ] : any } = { [ key : string ] : any }
21
+ CaseReducers extends SliceCaseReducerDefinitions < State , PayloadActions > = {
22
+ [ key : string ] : any
23
+ }
22
24
> {
23
25
/**
24
26
* The slice name.
@@ -34,15 +36,20 @@ export interface Slice<
34
36
* Action creators for the types of actions that are handled by the slice
35
37
* reducer.
36
38
*/
37
- actions : ActionCreators
39
+ actions : CaseReducerActions < CaseReducers >
40
+
41
+ caseReducers : SliceDefinedCaseReducers < CaseReducers , State >
38
42
}
39
43
40
44
/**
41
45
* Options for `createSlice()`.
42
46
*/
43
47
export interface CreateSliceOptions <
44
48
State = any ,
45
- CR extends SliceCaseReducers < State , any > = SliceCaseReducers < State , any >
49
+ CR extends SliceCaseReducerDefinitions <
50
+ State ,
51
+ any
52
+ > = SliceCaseReducerDefinitions < State , any >
46
53
> {
47
54
/**
48
55
* The slice's name. Used to namespace the generated action types.
@@ -74,23 +81,23 @@ type PayloadActions<Types extends keyof any = string> = Record<
74
81
PayloadAction
75
82
>
76
83
77
- type EnhancedCaseReducer < State , Action extends PayloadAction > = {
84
+ type CaseReducerWithPrepare < State , Action extends PayloadAction > = {
78
85
reducer : CaseReducer < State , Action >
79
86
prepare : PrepareAction < Action [ 'payload' ] >
80
87
}
81
88
82
- type SliceCaseReducers < State , PA extends PayloadActions > = {
89
+ type SliceCaseReducerDefinitions < State , PA extends PayloadActions > = {
83
90
[ ActionType in keyof PA ] :
84
91
| CaseReducer < State , PA [ ActionType ] >
85
- | EnhancedCaseReducer < State , PA [ ActionType ] >
92
+ | CaseReducerWithPrepare < State , PA [ ActionType ] >
86
93
}
87
94
88
95
type IfIsReducerFunctionWithoutAction < R , True , False = never > = R extends (
89
96
state : any
90
97
) => any
91
98
? True
92
99
: False
93
- type IfIsEnhancedReducer < R , True , False = never > = R extends {
100
+ type IfIsCaseReducerWithPrepare < R , True , False = never > = R extends {
94
101
prepare : Function
95
102
}
96
103
? True
@@ -106,8 +113,21 @@ type PrepareActionForReducer<R> = R extends { prepare: infer Prepare }
106
113
? Prepare
107
114
: never
108
115
109
- type CaseReducerActions < CaseReducers extends SliceCaseReducers < any , any > > = {
110
- [ Type in keyof CaseReducers ] : IfIsEnhancedReducer <
116
+ type ActionForReducer < R , S > = R extends (
117
+ state : S ,
118
+ action : PayloadAction < infer P >
119
+ ) => S
120
+ ? PayloadAction < P >
121
+ : R extends {
122
+ reducer ( state : any , action : PayloadAction < infer P > ) : any
123
+ }
124
+ ? PayloadAction < P >
125
+ : unknown
126
+
127
+ type CaseReducerActions <
128
+ CaseReducers extends SliceCaseReducerDefinitions < any , any >
129
+ > = {
130
+ [ Type in keyof CaseReducers ] : IfIsCaseReducerWithPrepare <
111
131
CaseReducers [ Type ] ,
112
132
ActionCreatorWithPreparedPayload <
113
133
PrepareActionForReducer < CaseReducers [ Type ] >
@@ -122,6 +142,16 @@ type CaseReducerActions<CaseReducers extends SliceCaseReducers<any, any>> = {
122
142
>
123
143
}
124
144
145
+ type SliceDefinedCaseReducers <
146
+ CaseReducers extends SliceCaseReducerDefinitions < any , any > ,
147
+ State = any
148
+ > = {
149
+ [ Type in keyof CaseReducers ] : CaseReducer <
150
+ State ,
151
+ ActionForReducer < CaseReducers [ Type ] , State >
152
+ >
153
+ }
154
+
125
155
type NoInfer < T > = [ T ] [ T extends any ? 0 : never ]
126
156
127
157
type SliceCaseReducersCheck < S , ACR > = {
@@ -134,9 +164,9 @@ type SliceCaseReducersCheck<S, ACR> = {
134
164
: { }
135
165
}
136
166
137
- type RestrictEnhancedReducersToMatchReducerAndPrepare <
167
+ type RestrictCaseReducerDefinitionsToMatchReducerAndPrepare <
138
168
S ,
139
- CR extends SliceCaseReducers < S , any >
169
+ CR extends SliceCaseReducerDefinitions < S , any >
140
170
> = { reducers : SliceCaseReducersCheck < S , NoInfer < CR > > }
141
171
142
172
function getType ( slice : string , actionKey : string ) : string {
@@ -153,54 +183,59 @@ function getType(slice: string, actionKey: string): string {
153
183
*/
154
184
export function createSlice <
155
185
State ,
156
- CaseReducers extends SliceCaseReducers < State , any >
186
+ CaseReducers extends SliceCaseReducerDefinitions < State , any >
157
187
> (
158
188
options : CreateSliceOptions < State , CaseReducers > &
159
- RestrictEnhancedReducersToMatchReducerAndPrepare < State , CaseReducers >
160
- ) : Slice < State , CaseReducerActions < CaseReducers > >
189
+ RestrictCaseReducerDefinitionsToMatchReducerAndPrepare < State , CaseReducers >
190
+ ) : Slice < State , CaseReducers >
161
191
162
192
// internal definition is a little less restrictive
163
193
export function createSlice <
164
194
State ,
165
- CaseReducers extends SliceCaseReducers < State , any >
195
+ CaseReducers extends SliceCaseReducerDefinitions < State , any >
166
196
> (
167
197
options : CreateSliceOptions < State , CaseReducers >
168
- ) : Slice < State , CaseReducerActions < CaseReducers > > {
198
+ ) : Slice < State , CaseReducers > {
169
199
const { name, initialState } = options
170
200
if ( ! name ) {
171
201
throw new Error ( '`name` is a required option for createSlice' )
172
202
}
173
203
const reducers = options . reducers || { }
174
204
const extraReducers = options . extraReducers || { }
175
- const actionKeys = Object . keys ( reducers )
176
-
177
- const reducerMap = actionKeys . reduce ( ( map , actionKey ) => {
178
- let maybeEnhancedReducer = reducers [ actionKey ]
179
- map [ getType ( name , actionKey ) ] =
180
- typeof maybeEnhancedReducer === 'function'
181
- ? maybeEnhancedReducer
182
- : maybeEnhancedReducer . reducer
183
- return map
184
- } , extraReducers )
185
-
186
- const reducer = createReducer ( initialState , reducerMap )
187
-
188
- const actionMap = actionKeys . reduce (
189
- ( map , action ) => {
190
- let maybeEnhancedReducer = reducers [ action ]
191
- const type = getType ( name , action )
192
- map [ action ] =
193
- typeof maybeEnhancedReducer === 'function'
194
- ? createAction ( type )
195
- : createAction ( type , maybeEnhancedReducer . prepare )
196
- return map
197
- } ,
198
- { } as any
199
- )
205
+ const reducerNames = Object . keys ( reducers )
206
+
207
+ const sliceCaseReducersByName : Record < string , CaseReducer > = { }
208
+ const sliceCaseReducersByType : Record < string , CaseReducer > = { }
209
+ const actionCreators : Record < string , PayloadActionCreator > = { }
210
+
211
+ reducerNames . forEach ( reducerName => {
212
+ const maybeReducerWithPrepare = reducers [ reducerName ]
213
+ const type = getType ( name , reducerName )
214
+
215
+ let caseReducer : CaseReducer < State , any >
216
+ let prepareCallback : PrepareAction < any > | undefined
217
+
218
+ if ( typeof maybeReducerWithPrepare === 'function' ) {
219
+ caseReducer = maybeReducerWithPrepare
220
+ } else {
221
+ caseReducer = maybeReducerWithPrepare . reducer
222
+ prepareCallback = maybeReducerWithPrepare . prepare
223
+ }
224
+
225
+ sliceCaseReducersByName [ reducerName ] = caseReducer
226
+ sliceCaseReducersByType [ type ] = caseReducer
227
+ actionCreators [ reducerName ] = prepareCallback
228
+ ? createAction ( type , prepareCallback )
229
+ : createAction ( type )
230
+ } )
231
+
232
+ const finalCaseReducers = { ...extraReducers , ...sliceCaseReducersByType }
233
+ const reducer = createReducer ( initialState , finalCaseReducers )
200
234
201
235
return {
202
236
name,
203
237
reducer,
204
- actions : actionMap
238
+ actions : actionCreators as any ,
239
+ caseReducers : sliceCaseReducersByName as any
205
240
}
206
241
}
0 commit comments