Skip to content

Commit a6d4308

Browse files
committed
Add PreloadedState generic
1 parent 44e4798 commit a6d4308

8 files changed

+170
-148
lines changed

src/applyMiddleware.ts

+19-25
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import compose from './compose'
22
import { Middleware, MiddlewareAPI } from './types/middleware'
3-
import { AnyAction } from './types/actions'
4-
import { StoreEnhancer, Dispatch, PreloadedState } from './types/store'
5-
import { Reducer } from './types/reducers'
3+
import { StoreEnhancer, Dispatch } from './types/store'
64

75
/**
86
* Creates a store enhancer that applies middleware to the dispatch method
@@ -55,29 +53,25 @@ export default function applyMiddleware<Ext, S = any>(
5553
export default function applyMiddleware(
5654
...middlewares: Middleware[]
5755
): StoreEnhancer<any> {
58-
return createStore =>
59-
<S, A extends AnyAction>(
60-
reducer: Reducer<S, A>,
61-
preloadedState?: PreloadedState<S>
62-
) => {
63-
const store = createStore(reducer, preloadedState)
64-
let dispatch: Dispatch = () => {
65-
throw new Error(
66-
'Dispatching while constructing your middleware is not allowed. ' +
67-
'Other middleware would not be applied to this dispatch.'
68-
)
69-
}
56+
return createStore => (reducer, preloadedState) => {
57+
const store = createStore(reducer, preloadedState)
58+
let dispatch: Dispatch = () => {
59+
throw new Error(
60+
'Dispatching while constructing your middleware is not allowed. ' +
61+
'Other middleware would not be applied to this dispatch.'
62+
)
63+
}
7064

71-
const middlewareAPI: MiddlewareAPI = {
72-
getState: store.getState,
73-
dispatch: (action, ...args) => dispatch(action, ...args)
74-
}
75-
const chain = middlewares.map(middleware => middleware(middlewareAPI))
76-
dispatch = compose<typeof dispatch>(...chain)(store.dispatch)
65+
const middlewareAPI: MiddlewareAPI = {
66+
getState: store.getState,
67+
dispatch: (action, ...args) => dispatch(action, ...args)
68+
}
69+
const chain = middlewares.map(middleware => middleware(middlewareAPI))
70+
dispatch = compose<typeof dispatch>(...chain)(store.dispatch)
7771

78-
return {
79-
...store,
80-
dispatch
81-
}
72+
return {
73+
...store,
74+
dispatch
8275
}
76+
}
8377
}

src/combineReducers.ts

+29-13
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { AnyAction, Action } from './types/actions'
22
import {
33
ActionFromReducersMapObject,
4+
PreloadedStateFromReducersMapObject,
45
Reducer,
56
ReducersMapObject,
67
StateFromReducersMapObject
78
} from './types/reducers'
8-
import { CombinedState } from './types/store'
99

1010
import ActionTypes from './utils/actionTypes'
1111
import isPlainObject from './utils/isPlainObject'
@@ -111,18 +111,32 @@ function assertReducerShape(reducers: ReducersMapObject) {
111111
* object, and builds a state object with the same shape.
112112
*/
113113
export default function combineReducers<S>(
114-
reducers: ReducersMapObject<S, any>
115-
): Reducer<CombinedState<S>>
116-
export default function combineReducers<S, A extends Action = AnyAction>(
117-
reducers: ReducersMapObject<S, A>
118-
): Reducer<CombinedState<S>, A>
114+
reducers: ReducersMapObject<S, any, any>
115+
): Reducer<S, AnyAction, Partial<S>>
116+
export default function combineReducers<S, PreloadedState = S>(
117+
reducers: ReducersMapObject<S, any, PreloadedState>
118+
): Reducer<S, AnyAction, Partial<PreloadedState>>
119+
export default function combineReducers<
120+
S,
121+
A extends Action = AnyAction,
122+
PreloadedState = S
123+
>(
124+
reducers: ReducersMapObject<S, A, PreloadedState>
125+
): Reducer<S, A, Partial<PreloadedState>>
119126
export default function combineReducers<M extends ReducersMapObject>(
120127
reducers: M
121128
): Reducer<
122-
CombinedState<StateFromReducersMapObject<M>>,
123-
ActionFromReducersMapObject<M>
129+
StateFromReducersMapObject<M>,
130+
ActionFromReducersMapObject<M>,
131+
Partial<PreloadedStateFromReducersMapObject<M>>
124132
>
125-
export default function combineReducers(reducers: ReducersMapObject) {
133+
export default function combineReducers<M extends ReducersMapObject>(
134+
reducers: M
135+
): Reducer<
136+
StateFromReducersMapObject<M>,
137+
ActionFromReducersMapObject<M>,
138+
Partial<PreloadedStateFromReducersMapObject<M>>
139+
> {
126140
const reducerKeys = Object.keys(reducers)
127141
const finalReducers: ReducersMapObject = {}
128142
for (let i = 0; i < reducerKeys.length; i++) {
@@ -155,7 +169,9 @@ export default function combineReducers(reducers: ReducersMapObject) {
155169
}
156170

157171
return function combination(
158-
state: StateFromReducersMapObject<typeof reducers> = {},
172+
state:
173+
| StateFromReducersMapObject<typeof reducers>
174+
| Partial<PreloadedStateFromReducersMapObject<typeof reducers>> = {},
159175
action: AnyAction
160176
) {
161177
if (shapeAssertionError) {
@@ -175,7 +191,7 @@ export default function combineReducers(reducers: ReducersMapObject) {
175191
}
176192

177193
let hasChanged = false
178-
const nextState: StateFromReducersMapObject<typeof reducers> = {}
194+
const nextState: Partial<StateFromReducersMapObject<typeof reducers>> = {}
179195
for (let i = 0; i < finalReducerKeys.length; i++) {
180196
const key = finalReducerKeys[i]
181197
const reducer = finalReducers[key]
@@ -191,11 +207,11 @@ export default function combineReducers(reducers: ReducersMapObject) {
191207
`If you want this reducer to hold no value, you can return null instead of undefined.`
192208
)
193209
}
194-
nextState[key] = nextStateForKey
210+
nextState[key as keyof typeof nextState] = nextStateForKey
195211
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
196212
}
197213
hasChanged =
198214
hasChanged || finalReducerKeys.length !== Object.keys(state).length
199-
return hasChanged ? nextState : state
215+
return (hasChanged ? nextState : state) as StateFromReducersMapObject<M>
200216
}
201217
}

src/createStore.ts

+17-12
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import $$observable from './utils/symbol-observable'
22

33
import {
44
Store,
5-
PreloadedState,
65
StoreEnhancer,
76
Dispatch,
87
Observer,
@@ -76,21 +75,23 @@ export function createStore<
7675
export function createStore<
7776
S,
7877
A extends Action,
78+
PreloadedState,
7979
Ext extends {} = {},
8080
StateExt extends {} = {}
8181
>(
82-
reducer: Reducer<S, A>,
83-
preloadedState?: PreloadedState<S>,
82+
reducer: Reducer<S, A, PreloadedState>,
83+
preloadedState?: PreloadedState | undefined,
8484
enhancer?: StoreEnhancer<Ext, StateExt>
8585
): Store<S, A, StateExt> & Ext
8686
export function createStore<
8787
S,
8888
A extends Action,
89+
PreloadedState,
8990
Ext extends {} = {},
9091
StateExt extends {} = {}
9192
>(
92-
reducer: Reducer<S, A>,
93-
preloadedState?: PreloadedState<S> | StoreEnhancer<Ext, StateExt>,
93+
reducer: Reducer<S, A, PreloadedState>,
94+
preloadedState?: PreloadedState | StoreEnhancer<Ext, StateExt> | undefined,
9495
enhancer?: StoreEnhancer<Ext, StateExt>
9596
): Store<S, A, StateExt> & Ext {
9697
if (typeof reducer !== 'function') {
@@ -128,12 +129,14 @@ export function createStore<
128129

129130
return enhancer(createStore)(
130131
reducer,
131-
preloadedState as PreloadedState<S>
132-
) as Store<S, A, StateExt> & Ext
132+
preloadedState as PreloadedState | undefined
133+
)
133134
}
134135

135136
let currentReducer = reducer
136-
let currentState = preloadedState as S
137+
let currentState: S | PreloadedState | undefined = preloadedState as
138+
| PreloadedState
139+
| undefined
137140
let currentListeners: Map<number, ListenerCallback> | null = new Map()
138141
let nextListeners = currentListeners
139142
let listenerIdCounter = 0
@@ -315,7 +318,7 @@ export function createStore<
315318
)
316319
}
317320

318-
currentReducer = nextReducer
321+
currentReducer = nextReducer as unknown as Reducer<S, A, PreloadedState>
319322

320323
// This action has a similar effect to ActionTypes.INIT.
321324
// Any reducers that existed in both the new and old rootReducer
@@ -455,21 +458,23 @@ export function legacy_createStore<
455458
export function legacy_createStore<
456459
S,
457460
A extends Action,
461+
PreloadedState,
458462
Ext extends {} = {},
459463
StateExt extends {} = {}
460464
>(
461-
reducer: Reducer<S, A>,
462-
preloadedState?: PreloadedState<S>,
465+
reducer: Reducer<S, A, PreloadedState>,
466+
preloadedState?: PreloadedState | undefined,
463467
enhancer?: StoreEnhancer<Ext, StateExt>
464468
): Store<S, A, StateExt> & Ext
465469
export function legacy_createStore<
466470
S,
467471
A extends Action,
472+
PreloadedState,
468473
Ext extends {} = {},
469474
StateExt extends {} = {}
470475
>(
471476
reducer: Reducer<S, A>,
472-
preloadedState?: PreloadedState<S> | StoreEnhancer<Ext, StateExt>,
477+
preloadedState?: PreloadedState | StoreEnhancer<Ext, StateExt> | undefined,
473478
enhancer?: StoreEnhancer<Ext, StateExt>
474479
): Store<S, A, StateExt> & Ext {
475480
return createStore(reducer, preloadedState as any, enhancer)

src/index.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ import __DO_NOT_USE__ActionTypes from './utils/actionTypes'
99
// types
1010
// store
1111
export {
12-
CombinedState,
13-
PreloadedState,
1412
Dispatch,
1513
Unsubscribe,
1614
Observable,
@@ -23,9 +21,10 @@ export {
2321
// reducers
2422
export {
2523
Reducer,
26-
ReducerFromReducersMapObject,
2724
ReducersMapObject,
2825
StateFromReducersMapObject,
26+
PreloadedStateFromReducersMapObject,
27+
ReducerFromReducersMapObject,
2928
ActionFromReducer,
3029
ActionFromReducersMapObject
3130
} from './types/reducers'

src/types/reducers.ts

+53-20
Original file line numberDiff line numberDiff line change
@@ -25,55 +25,88 @@ import { Action, AnyAction } from './actions'
2525
*
2626
* @template S The type of state consumed and produced by this reducer.
2727
* @template A The type of actions the reducer can potentially respond to.
28+
* @template PreloadedState The type of state consumed by this reducer the first time it's called.
2829
*/
29-
export type Reducer<S = any, A extends Action = AnyAction> = (
30-
state: S | undefined,
31-
action: A
32-
) => S
30+
export type Reducer<
31+
S = any,
32+
A extends Action = AnyAction,
33+
PreloadedState = S
34+
> = (state: S | PreloadedState | undefined, action: A) => S
3335

3436
/**
3537
* Object whose values correspond to different reducer functions.
3638
*
39+
* @template S The combined state of the reducers.
3740
* @template A The type of actions the reducers can potentially respond to.
41+
* @template PreloadedState The combined preloaded state of the reducers.
3842
*/
39-
export type ReducersMapObject<S = any, A extends Action = AnyAction> = {
40-
[K in keyof S]: Reducer<S[K], A>
43+
export type ReducersMapObject<
44+
S = any,
45+
A extends Action = AnyAction,
46+
PreloadedState = S
47+
> = {
48+
[K in keyof S]: Reducer<
49+
S[K],
50+
A,
51+
K extends keyof PreloadedState ? PreloadedState[K] : never
52+
>
4153
}
4254

4355
/**
4456
* Infer a combined state shape from a `ReducersMapObject`.
4557
*
4658
* @template M Object map of reducers as provided to `combineReducers(map: M)`.
4759
*/
48-
export type StateFromReducersMapObject<M> = M extends ReducersMapObject
49-
? { [P in keyof M]: M[P] extends Reducer<infer S, any> ? S : never }
50-
: never
60+
export type StateFromReducersMapObject<
61+
M extends ReducersMapObject<any, any, any>
62+
> = {
63+
[P in keyof M]: M[P] extends Reducer<infer S, any, any> | undefined
64+
? S
65+
: never
66+
}
5167

5268
/**
53-
* Infer reducer union type from a `ReducersMapObject`.
69+
* Infer a combined preloaded state shape from a `ReducersMapObject`.
5470
*
5571
* @template M Object map of reducers as provided to `combineReducers(map: M)`.
5672
*/
57-
export type ReducerFromReducersMapObject<M> = M extends {
58-
[P in keyof M]: infer R
59-
}
60-
? R extends Reducer<any, any>
61-
? R
73+
export type PreloadedStateFromReducersMapObject<
74+
M extends ReducersMapObject<any, any, any>
75+
> = {
76+
[P in keyof M]: M[P] extends
77+
| Reducer<any, any, infer PreloadedState>
78+
| undefined
79+
? PreloadedState
6280
: never
63-
: never
81+
}
82+
83+
/**
84+
* Infer reducer union type from a `ReducersMapObject`.
85+
*
86+
* @template M Object map of reducers as provided to `combineReducers(map: M)`.
87+
*/
88+
export type ReducerFromReducersMapObject<
89+
M extends ReducersMapObject<any, any, any>
90+
> = M[keyof M]
6491

6592
/**
6693
* Infer action type from a reducer function.
6794
*
6895
* @template R Type of reducer.
6996
*/
70-
export type ActionFromReducer<R> = R extends Reducer<any, infer A> ? A : never
97+
export type ActionFromReducer<R extends Reducer<any, any>> = R extends Reducer<
98+
any,
99+
infer A,
100+
any
101+
>
102+
? A
103+
: never
71104

72105
/**
73106
* Infer action union type from a `ReducersMapObject`.
74107
*
75108
* @template M Object map of reducers as provided to `combineReducers(map: M)`.
76109
*/
77-
export type ActionFromReducersMapObject<M> = M extends ReducersMapObject
78-
? ActionFromReducer<ReducerFromReducersMapObject<M>>
79-
: never
110+
export type ActionFromReducersMapObject<
111+
M extends ReducersMapObject<any, any, any>
112+
> = ActionFromReducer<ReducerFromReducersMapObject<M>>

0 commit comments

Comments
 (0)