Skip to content

Commit f082616

Browse files
committed
WIP check the type of the state
1 parent 7b3315e commit f082616

File tree

2 files changed

+43
-22
lines changed

2 files changed

+43
-22
lines changed

src/combineReducers.js

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,16 @@ function getUndefinedStateErrorMessage(key, action) {
1212
)
1313
}
1414

15+
function getInputStateTypeString(inputState) {
16+
var inputMatch = inputState.toString().match(/(.*?)\s/)
17+
var typeName = typeof inputState
18+
return typeName === 'object'
19+
? isPlainObject(inputState)
20+
? ({}).toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1]
21+
: inputMatch && inputMatch[0]
22+
: typeName
23+
}
24+
1525
function getUnexpectedStateShapeWarningMessage(inputState, reducers, action) {
1626
var reducerKeys = Object.keys(reducers)
1727
var argumentName = action && action.type === ActionTypes.INIT ?
@@ -25,14 +35,14 @@ function getUnexpectedStateShapeWarningMessage(inputState, reducers, action) {
2535
)
2636
}
2737

28-
if (!isPlainObject(inputState)) {
29-
return (
30-
`The ${argumentName} has unexpected type of "` +
31-
({}).toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1] +
32-
`". Expected argument to be an object with the following ` +
33-
`keys: "${reducerKeys.join('", "')}"`
34-
)
35-
}
38+
// if (!isPlainObject(inputState)) {
39+
// return (
40+
// `The ${argumentName} has unexpected type of "` +
41+
// ({}).toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1] +
42+
// `". Expected argument to be an object with the following ` +
43+
// `keys: "${reducerKeys.join('", "')}"`
44+
// )
45+
// }
3646

3747
var unexpectedKeys = Object.keys(inputState).filter(key => !reducers.hasOwnProperty(key))
3848

@@ -105,7 +115,7 @@ export default function combineReducers(reducers, options = {
105115
set: (state, key, stateForKey) => {
106116
state[key] = stateForKey
107117
return state
108-
},
118+
}
109119
}) {
110120
var reducerKeys = Object.keys(reducers)
111121
var finalReducers = {}
@@ -125,12 +135,18 @@ export default function combineReducers(reducers, options = {
125135
sanityError = e
126136
}
127137

138+
var initialTypeString = getInputStateTypeString(options.create({}))
139+
128140
return function combination(state = options.create({}), action) {
129141
if (sanityError) {
130142
throw sanityError
131143
}
132144

133145
if (process.env.NODE_ENV !== 'production') {
146+
var currentStateTypeString = getInputStateTypeString(state)
147+
if (initialTypeString !== currentStateTypeString) {
148+
throw new Error(`The state has type ${currentStateTypeString} but was expecting ${initialTypeString}`)
149+
}
134150
var warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action)
135151
if (warningMessage) {
136152
warning(warningMessage)

test/combineReducers.spec.js

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,15 @@ describe('Utils', () => {
3535
const s1 = reducer(undefined, { type: 'increment' })
3636
expect(s1.get('counter')).toEqual(1)
3737
expect(s1.get('stack')).toEqual([])
38+
3839
const s2 = reducer(s1, { type: 'push', value: 'a' })
39-
expect(s2.get("counter")).toEqual(1)
40-
expect(s2.get("stack")).toEqual([ 'a' ])
40+
expect(s2.get('counter')).toEqual(1)
41+
expect(s2.get('stack')).toEqual([ 'a' ])
42+
43+
// when we supply a previousState with a different type we get a warning
44+
// expect(
45+
// () => reducer({ stack: [], counter: 0 }, { type: 'push', value: 'b' })
46+
// ).toThrow(/The state has type Object but was expecting Map/)
4147
})
4248

4349
it('ignores all props which are not a function', () => {
@@ -215,9 +221,6 @@ describe('Utils', () => {
215221
}
216222
})
217223

218-
reducer()
219-
expect(spy.calls.length).toBe(0)
220-
221224
reducer({ foo: { bar: 2 } })
222225
expect(spy.calls.length).toBe(0)
223226

@@ -237,24 +240,26 @@ describe('Utils', () => {
237240
/Unexpected keys "bar", "qux".*createStore.*instead: "foo", "baz"/
238241
)
239242

240-
createStore(reducer, 1)
241-
expect(spy.calls[2].arguments[0]).toMatch(
242-
/createStore has unexpected type of "Number".*keys: "foo", "baz"/
243+
expect(
244+
() => createStore(reducer, 1)
245+
).toThrow(
246+
/The state has type number but was expecting Object/
243247
)
244248

245249
reducer({ bar: 2 })
246-
expect(spy.calls[3].arguments[0]).toMatch(
250+
expect(spy.calls[2].arguments[0]).toMatch(
247251
/Unexpected key "bar".*reducer.*instead: "foo", "baz"/
248252
)
249253

250254
reducer({ bar: 2, qux: 4 })
251-
expect(spy.calls[4].arguments[0]).toMatch(
255+
expect(spy.calls[3].arguments[0]).toMatch(
252256
/Unexpected keys "bar", "qux".*reducer.*instead: "foo", "baz"/
253257
)
254258

255-
reducer(1)
256-
expect(spy.calls[5].arguments[0]).toMatch(
257-
/reducer has unexpected type of "Number".*keys: "foo", "baz"/
259+
expect(
260+
() => reducer(1)
261+
).toThrow(
262+
/The state has type number but was expecting Object/
258263
)
259264

260265
spy.restore()

0 commit comments

Comments
 (0)