Skip to content

Commit ac804a5

Browse files
committed
Add PreloadedState generic
1 parent f503238 commit ac804a5

11 files changed

+268
-176
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

+17-17
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import { AnyAction, Action } from './types/actions'
22
import {
33
ActionFromReducersMapObject,
4+
PreloadedStateShapeFromReducersMapObject,
45
Reducer,
5-
ReducersMapObject,
66
StateFromReducersMapObject
77
} from './types/reducers'
8-
import { CombinedState } from './types/store'
98

109
import ActionTypes from './utils/actionTypes'
1110
import isPlainObject from './utils/isPlainObject'
@@ -14,7 +13,7 @@ import { kindOf } from './utils/kindOf'
1413

1514
function getUnexpectedStateShapeWarningMessage(
1615
inputState: object,
17-
reducers: ReducersMapObject,
16+
reducers: { [key: string]: Reducer<any, any, any> },
1817
action: Action,
1918
unexpectedKeyCache: { [key: string]: true }
2019
) {
@@ -60,7 +59,9 @@ function getUnexpectedStateShapeWarningMessage(
6059
}
6160
}
6261

63-
function assertReducerShape(reducers: ReducersMapObject) {
62+
function assertReducerShape(reducers: {
63+
[key: string]: Reducer<any, any, any>
64+
}) {
6465
Object.keys(reducers).forEach(key => {
6566
const reducer = reducers[key]
6667
const initialState = reducer(undefined, { type: ActionTypes.INIT })
@@ -110,21 +111,20 @@ function assertReducerShape(reducers: ReducersMapObject) {
110111
* @returns A reducer function that invokes every reducer inside the passed
111112
* object, and builds a state object with the same shape.
112113
*/
113-
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>
119-
export default function combineReducers<M extends ReducersMapObject>(
114+
export default function combineReducers<M>(
120115
reducers: M
121-
): Reducer<
122-
CombinedState<StateFromReducersMapObject<M>>,
123-
ActionFromReducersMapObject<M>
124-
>
125-
export default function combineReducers(reducers: ReducersMapObject) {
116+
): M[keyof M] extends Reducer<any, any, any> | undefined
117+
? Reducer<
118+
StateFromReducersMapObject<M>,
119+
ActionFromReducersMapObject<M>,
120+
Partial<PreloadedStateShapeFromReducersMapObject<M>>
121+
>
122+
: never
123+
export default function combineReducers(reducers: {
124+
[key: string]: Reducer<any, any, any>
125+
}) {
126126
const reducerKeys = Object.keys(reducers)
127-
const finalReducers: ReducersMapObject = {}
127+
const finalReducers: { [key: string]: Reducer<any, any, any> } = {}
128128
for (let i = 0; i < reducerKeys.length; i++) {
129129
const key = reducerKeys[i]
130130

src/createStore.ts

+21-16
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,
@@ -77,20 +76,22 @@ export function createStore<
7776
S,
7877
A extends Action,
7978
Ext extends {} = {},
80-
StateExt extends {} = {}
79+
StateExt extends {} = {},
80+
PreloadedState = S
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,
8989
Ext extends {} = {},
90-
StateExt extends {} = {}
90+
StateExt extends {} = {},
91+
PreloadedState = S
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
@@ -456,20 +459,22 @@ export function legacy_createStore<
456459
S,
457460
A extends Action,
458461
Ext extends {} = {},
459-
StateExt extends {} = {}
462+
StateExt extends {} = {},
463+
PreloadedState = S
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,
468472
Ext extends {} = {},
469-
StateExt extends {} = {}
473+
StateExt extends {} = {},
474+
PreloadedState = S
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

+3-4
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,11 +21,12 @@ export {
2321
// reducers
2422
export {
2523
Reducer,
26-
ReducerFromReducersMapObject,
2724
ReducersMapObject,
2825
StateFromReducersMapObject,
26+
ReducerFromReducersMapObject,
2927
ActionFromReducer,
30-
ActionFromReducersMapObject
28+
ActionFromReducersMapObject,
29+
PreloadedStateShapeFromReducersMapObject
3130
} from './types/reducers'
3231
// action creators
3332
export { ActionCreator, ActionCreatorsMapObject } from './types/actions'

src/types/reducers.ts

+56-18
Original file line numberDiff line numberDiff line change
@@ -25,55 +25,93 @@ 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>
41-
}
43+
export type ReducersMapObject<
44+
S = any,
45+
A extends Action = AnyAction,
46+
PreloadedState = S
47+
> = keyof PreloadedState extends keyof S
48+
? {
49+
[K in keyof S]: Reducer<
50+
S[K],
51+
A,
52+
K extends keyof PreloadedState ? PreloadedState[K] : never
53+
>
54+
}
55+
: never
4256

4357
/**
4458
* Infer a combined state shape from a `ReducersMapObject`.
4559
*
4660
* @template M Object map of reducers as provided to `combineReducers(map: M)`.
4761
*/
48-
export type StateFromReducersMapObject<M> = M extends ReducersMapObject
49-
? { [P in keyof M]: M[P] extends Reducer<infer S, any> ? S : never }
62+
export type StateFromReducersMapObject<M> = M[keyof M] extends
63+
| Reducer<any, any, any>
64+
| undefined
65+
? {
66+
[P in keyof M]: M[P] extends Reducer<infer S> ? S : never
67+
}
5068
: never
5169

5270
/**
5371
* Infer reducer union type from a `ReducersMapObject`.
5472
*
5573
* @template M Object map of reducers as provided to `combineReducers(map: M)`.
5674
*/
57-
export type ReducerFromReducersMapObject<M> = M extends {
58-
[P in keyof M]: infer R
59-
}
60-
? R extends Reducer<any, any>
61-
? R
62-
: never
75+
export type ReducerFromReducersMapObject<M> = M[keyof M] extends
76+
| Reducer<any, any, any>
77+
| undefined
78+
? M[keyof M]
6379
: never
6480

6581
/**
6682
* Infer action type from a reducer function.
6783
*
6884
* @template R Type of reducer.
6985
*/
70-
export type ActionFromReducer<R> = R extends Reducer<any, infer A> ? A : never
86+
export type ActionFromReducer<R> = R extends
87+
| Reducer<any, infer A, any>
88+
| undefined
89+
? A
90+
: never
7191

7292
/**
7393
* Infer action union type from a `ReducersMapObject`.
7494
*
7595
* @template M Object map of reducers as provided to `combineReducers(map: M)`.
7696
*/
77-
export type ActionFromReducersMapObject<M> = M extends ReducersMapObject
78-
? ActionFromReducer<ReducerFromReducersMapObject<M>>
97+
export type ActionFromReducersMapObject<M> = ActionFromReducer<
98+
ReducerFromReducersMapObject<M>
99+
>
100+
101+
/**
102+
* Infer a combined preloaded state shape from a `ReducersMapObject`.
103+
*
104+
* @template M Object map of reducers as provided to `combineReducers(map: M)`.
105+
*/
106+
export type PreloadedStateShapeFromReducersMapObject<M> = M[keyof M] extends
107+
| Reducer<any, any, any>
108+
| undefined
109+
? {
110+
[P in keyof M]: M[P] extends (
111+
inputState: infer InputState,
112+
action: AnyAction
113+
) => any
114+
? InputState
115+
: never
116+
}
79117
: never

0 commit comments

Comments
 (0)