Skip to content

Commit 456cc34

Browse files
committed
Encourage use of Action or UnknownAction instead of AnyAction
1 parent cbde7c9 commit 456cc34

16 files changed

+118
-82
lines changed

.gitignore

-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ coverage
55
dist
66
lib
77
es
8-
types
98

109
# Yarn
1110
.cache

docs/usage/UsageWithTypescript.md

+7-7
Original file line numberDiff line numberDiff line change
@@ -207,10 +207,10 @@ You could add this to your ESLint config as an example:
207207

208208
[Reducers](../tutorials/fundamentals/part-3-state-actions-reducers.md) are pure functions that receive the current `state` and incoming `action` as arguments, and return a new state.
209209

210-
If you are using Redux Toolkit's `createSlice`, you should rarely need to specifically type a reducer separately. If you do actually write a standalone reducer, it's typically sufficient to declare the type of the `initialState` value, and type the `action` as `AnyAction`:
210+
If you are using Redux Toolkit's `createSlice`, you should rarely need to specifically type a reducer separately. If you do actually write a standalone reducer, it's typically sufficient to declare the type of the `initialState` value, and type the `action` as `UnknownAction`:
211211

212212
```ts
213-
import { AnyAction } from 'redux'
213+
import { UnknownAction } from 'redux'
214214

215215
interface CounterState {
216216
value: number
@@ -222,7 +222,7 @@ const initialState: CounterState = {
222222

223223
export default function counterReducer(
224224
state = initialState,
225-
action: AnyAction
225+
action: UnknownAction
226226
) {
227227
// logic here
228228
}
@@ -297,16 +297,16 @@ export type ThunkAction<
297297
> = (dispatch: ThunkDispatch<S, E, A>, getState: () => S, extraArgument: E) => R
298298
```
299299
300-
You will typically want to provide the `R` (return type) and `S` (state) generic arguments. Unfortunately, TS does not allow only providing _some_ generic arguments, so the usual values for the other arguments are `unknown` for `E` and `AnyAction` for `A`:
300+
You will typically want to provide the `R` (return type) and `S` (state) generic arguments. Unfortunately, TS does not allow only providing _some_ generic arguments, so the usual values for the other arguments are `unknown` for `E` and `UnknownAction` for `A`:
301301
302302
```ts
303-
import { AnyAction } from 'redux'
303+
import { UnknownAction } from 'redux'
304304
import { sendMessage } from './store/chat/actions'
305305
import { RootState } from './store'
306306
import { ThunkAction } from 'redux-thunk'
307307

308308
export const thunkSendMessage =
309-
(message: string): ThunkAction<void, RootState, unknown, AnyAction> =>
309+
(message: string): ThunkAction<void, RootState, unknown, UnknownAction> =>
310310
async dispatch => {
311311
const asyncResp = await exampleAPI()
312312
dispatch(
@@ -330,7 +330,7 @@ export type AppThunk<ReturnType = void> = ThunkAction<
330330
ReturnType,
331331
RootState,
332332
unknown,
333-
AnyAction
333+
UnknownAction
334334
>
335335
```
336336

docs/usage/migrating-to-modern-redux.mdx

+1-1
Original file line numberDiff line numberDiff line change
@@ -846,7 +846,7 @@ const store = configureStore({
846846
// Inferred state type: {todos: TodosState, counter: CounterState}
847847
export type RootState = ReturnType<typeof store.getState>
848848
849-
// Inferred dispatch type: Dispatch & ThunkDispatch<RootState, undefined, AnyAction>
849+
// Inferred dispatch type: Dispatch & ThunkDispatch<RootState, undefined, UnknownAction>
850850
export type AppDispatch = typeof store.dispatch
851851
// highlight-end
852852
```

src/bindActionCreators.ts

+2-6
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
11
import { Dispatch } from './types/store'
2-
import {
3-
AnyAction,
4-
ActionCreator,
5-
ActionCreatorsMapObject
6-
} from './types/actions'
2+
import { ActionCreator, ActionCreatorsMapObject, Action } from './types/actions'
73
import { kindOf } from './utils/kindOf'
84

9-
function bindActionCreator<A extends AnyAction = AnyAction>(
5+
function bindActionCreator<A extends Action>(
106
actionCreator: ActionCreator<A>,
117
dispatch: Dispatch
128
) {

src/combineReducers.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { AnyAction, Action } from './types/actions'
1+
import { Action } from './types/actions'
22
import {
33
ActionFromReducersMapObject,
44
PreloadedStateShapeFromReducersMapObject,
@@ -156,7 +156,7 @@ export default function combineReducers(reducers: {
156156

157157
return function combination(
158158
state: StateFromReducersMapObject<typeof reducers> = {},
159-
action: AnyAction
159+
action: Action
160160
) {
161161
if (shapeAssertionError) {
162162
throw shapeAssertionError

src/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export { ActionCreator, ActionCreatorsMapObject } from './types/actions'
3333
// middleware
3434
export { MiddlewareAPI, Middleware } from './types/middleware'
3535
// actions
36-
export { Action, AnyAction } from './types/actions'
36+
export { Action, UnknownAction, AnyAction } from './types/actions'
3737

3838
export {
3939
createStore,

src/types/actions.ts

+13-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*
1616
* @template T the type of the action's `type` tag.
1717
*/
18-
export interface Action<T = any> {
18+
export interface Action<T = unknown> {
1919
type: T
2020
}
2121

@@ -25,6 +25,18 @@ export interface Action<T = any> {
2525
* This is not part of `Action` itself to prevent types that extend `Action` from
2626
* having an index signature.
2727
*/
28+
export interface UnknownAction extends Action {
29+
// Allows any extra properties to be defined in an action.
30+
[extraProps: string]: unknown
31+
}
32+
33+
/**
34+
* An Action type which accepts any other properties.
35+
* This is mainly for the use of the `Reducer` type.
36+
* This is not part of `Action` itself to prevent types that extend `Action` from
37+
* having an index signature.
38+
* @deprecated use Action or UnknownAction instead
39+
*/
2840
export interface AnyAction extends Action {
2941
// Allows any extra properties to be defined in an action.
3042
[extraProps: string]: any

src/types/reducers.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Action, AnyAction } from './actions'
1+
import { Action, UnknownAction } from './actions'
22

33
/* reducers */
44

@@ -29,7 +29,7 @@ import { Action, AnyAction } from './actions'
2929
*/
3030
export type Reducer<
3131
S = any,
32-
A extends Action = AnyAction,
32+
A extends Action = UnknownAction,
3333
PreloadedState = S
3434
> = (state: S | PreloadedState | undefined, action: A) => S
3535

@@ -42,7 +42,7 @@ export type Reducer<
4242
*/
4343
export type ReducersMapObject<
4444
S = any,
45-
A extends Action = AnyAction,
45+
A extends Action = UnknownAction,
4646
PreloadedState = S
4747
> = keyof PreloadedState extends keyof S
4848
? {
@@ -63,7 +63,7 @@ export type StateFromReducersMapObject<M> = M[keyof M] extends
6363
| Reducer<any, any, any>
6464
| undefined
6565
? {
66-
[P in keyof M]: M[P] extends Reducer<infer S> ? S : never
66+
[P in keyof M]: M[P] extends Reducer<infer S, any, any> ? S : never
6767
}
6868
: never
6969

@@ -109,7 +109,7 @@ export type PreloadedStateShapeFromReducersMapObject<M> = M[keyof M] extends
109109
? {
110110
[P in keyof M]: M[P] extends (
111111
inputState: infer InputState,
112-
action: AnyAction
112+
action: UnknownAction
113113
) => any
114114
? InputState
115115
: never

test/applyMiddleware.spec.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import {
33
applyMiddleware,
44
Middleware,
55
MiddlewareAPI,
6-
AnyAction,
76
Action,
87
Store,
98
Dispatch
@@ -128,7 +127,7 @@ describe('applyMiddleware', () => {
128127
const spy = vi.fn()
129128
const testCallArgs = ['test']
130129

131-
interface MultiDispatch<A extends Action = AnyAction> {
130+
interface MultiDispatch<A extends Action = Action> {
132131
<T extends A>(action: T, extraArg?: string[]): T
133132
}
134133

test/combineReducers.spec.ts

+9-10
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import {
44
combineReducers,
55
Reducer,
66
Action,
7-
AnyAction,
87
__DO_NOT_USE__ActionTypes as ActionTypes
98
} from 'redux'
109
import { vi } from 'vitest'
@@ -88,7 +87,7 @@ describe('Utils', () => {
8887
expect(() => reducer({ counter: 0 }, null)).toThrow(
8988
/"counter".*an action/
9089
)
91-
expect(() => reducer({ counter: 0 }, {} as unknown as AnyAction)).toThrow(
90+
expect(() => reducer({ counter: 0 }, {} as unknown as Action)).toThrow(
9291
/"counter".*an action/
9392
)
9493
})
@@ -117,9 +116,9 @@ describe('Utils', () => {
117116
throw new Error('Error thrown in reducer')
118117
}
119118
})
120-
expect(() =>
121-
reducer(undefined, undefined as unknown as AnyAction)
122-
).toThrow(/Error thrown in reducer/)
119+
expect(() => reducer(undefined, undefined as unknown as Action)).toThrow(
120+
/Error thrown in reducer/
121+
)
123122
})
124123

125124
it('allows a symbol to be used as an action type', () => {
@@ -185,7 +184,7 @@ describe('Utils', () => {
185184

186185
it('throws an error on first call if a reducer attempts to handle a private action', () => {
187186
const reducer = combineReducers({
188-
counter(state: number, action: Action<unknown>) {
187+
counter(state: number, action: Action) {
189188
switch (action.type) {
190189
case 'increment':
191190
return state + 1
@@ -199,9 +198,9 @@ describe('Utils', () => {
199198
}
200199
}
201200
})
202-
expect(() =>
203-
reducer(undefined, undefined as unknown as AnyAction)
204-
).toThrow(/"counter".*private/)
201+
expect(() => reducer(undefined, undefined as unknown as Action)).toThrow(
202+
/"counter".*private/
203+
)
205204
})
206205

207206
it('warns if no reducers are passed to combineReducers', () => {
@@ -223,7 +222,7 @@ describe('Utils', () => {
223222
it('warns if input state does not match reducer shape', () => {
224223
const preSpy = console.error
225224
const spy = vi.fn()
226-
const nullAction = undefined as unknown as AnyAction
225+
const nullAction = undefined as unknown as Action
227226
console.error = spy
228227

229228
interface ShapeState {

0 commit comments

Comments
 (0)