Skip to content

Commit db169a4

Browse files
committed
Make createReducer() return Reducer<Immutable<S>, A>
This reflects the actual runtime behavior of immer (which freezes produced values) and fixes a type error with immer >= 1.10.3.
1 parent a680049 commit db169a4

File tree

5 files changed

+44
-21
lines changed

5 files changed

+44
-21
lines changed

package-lock.json

Lines changed: 6 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
"index.d.ts"
4848
],
4949
"dependencies": {
50-
"immer": "^1.9.3",
50+
"immer": "^1.10.3",
5151
"redux": "^4.0.0",
5252
"redux-devtools-extension": "^2.13.7",
5353
"redux-immutable-state-invariant": "^2.1.0",

src/createReducer.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import createNextState from 'immer'
1+
import createNextState, { Draft, Immutable } from 'immer'
22
import { AnyAction, Action, Reducer } from 'redux'
33

44
/**
@@ -16,7 +16,7 @@ import { AnyAction, Action, Reducer } from 'redux'
1616
* translated to copy operations that result in a new state.
1717
*/
1818
export type CaseReducer<S = any, A extends Action = AnyAction> = (
19-
state: S,
19+
state: Draft<S>,
2020
action: A
2121
) => S | void
2222

@@ -44,13 +44,16 @@ export interface CaseReducersMapObject<S = any, A extends Action = AnyAction> {
4444
* case redeucers.
4545
*/
4646
export function createReducer<S = any, A extends Action = AnyAction>(
47-
initialState: S,
47+
initialState: Immutable<S>,
4848
actionsMap: CaseReducersMapObject<S, A>
49-
): Reducer<S, A> {
50-
return function(state = initialState, action): S {
51-
return createNextState(state, (draft: S) => {
49+
): Reducer<Immutable<S>, A> {
50+
return function(state = initialState, action): Immutable<S> {
51+
// @ts-ignore createNextState() produces an Immutable<Draft<S>> rather
52+
// than an Immutable<S>, and TypeScript cannot find out how to reconcile
53+
// these two types.
54+
return createNextState(state, (draft: Draft<S>) => {
5255
const caseReducer = actionsMap[action.type]
53-
return caseReducer ? caseReducer(draft as S, action) : draft
56+
return caseReducer ? caseReducer(draft, action) : undefined
5457
})
5558
}
5659
}

src/createSlice.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { Immutable } from 'immer'
12
import { Action, AnyAction, ActionCreator, Reducer } from 'redux'
23
import { createAction, PayloadAction } from './createAction'
34
import { createReducer, CaseReducersMapObject } from './createReducer'
@@ -50,7 +51,7 @@ export interface CreateSliceOptions<
5051
/**
5152
* The initial state to be returned by the slice reducer.
5253
*/
53-
initialState: S
54+
initialState: Immutable<S>
5455

5556
/**
5657
* A mapping from action types to action-type-specific *case reducer*
@@ -78,7 +79,7 @@ export function createSlice<
7879
CR extends CaseReducersMapObject<S, A> = CaseReducersMapObject<S, A>
7980
>(
8081
options: CreateSliceOptions<S, A, CR>
81-
): Slice<S, A, Extract<keyof CR, string>> {
82+
): Slice<Immutable<S>, A, Extract<keyof CR, string>> {
8283
const { slice = '', initialState } = options
8384
const reducers = options.reducers || {}
8485
const actionKeys = Object.keys(reducers)

type-tests/files/createReducer.typetest.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,27 @@ import { AnyAction, createReducer, Reducer } from 'redux-starter-kit'
5656
decrement: decrementHandler
5757
})
5858
}
59+
60+
/*
61+
* Test: createReducer() reducers are typed to return readonly states.
62+
*/
63+
{
64+
const initialCounters = {
65+
a: 0,
66+
b: 0
67+
}
68+
69+
const reducer = createReducer(initialCounters, {
70+
incrementA: state => {
71+
state.a += 1
72+
},
73+
incrementB: state => {
74+
state.b += 1
75+
}
76+
})
77+
78+
const newCounters = reducer(initialCounters, { type: 'incrementA' })
79+
80+
// typings:expect-error
81+
newCounters.a++
82+
}

0 commit comments

Comments
 (0)