Skip to content

Commit 748a32b

Browse files
author
ben.durrant
committed
account for new Middleware typing
1 parent 8332c84 commit 748a32b

File tree

7 files changed

+48
-22
lines changed

7 files changed

+48
-22
lines changed

packages/toolkit/src/createAction.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -286,15 +286,19 @@ export function createAction(type: string, prepareAction?: Function): any {
286286
return actionCreator
287287
}
288288

289+
export function isAction(action: unknown): action is Action<unknown> {
290+
return isPlainObject(action) && 'type' in action
291+
}
292+
289293
export function isFSA(action: unknown): action is {
290294
type: string
291295
payload?: unknown
292296
error?: unknown
293297
meta?: unknown
294298
} {
295299
return (
296-
isPlainObject(action) &&
297-
typeof (action as any).type === 'string' &&
300+
isAction(action) &&
301+
typeof action.type === 'string' &&
298302
Object.keys(action).every(isValidKey)
299303
)
300304
}

packages/toolkit/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ export {
3232
// js
3333
createAction,
3434
getType,
35+
isAction,
36+
isFSA as isFluxStandardAction,
3537
} from './createAction'
3638
export type {
3739
// types

packages/toolkit/src/listenerMiddleware/index.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { Dispatch, AnyAction, MiddlewareAPI } from 'redux'
22
import type { ThunkDispatch } from 'redux-thunk'
3-
import { createAction } from '../createAction'
3+
import { createAction, isAction } from '../createAction'
44
import { nanoid } from '../nanoid'
55

66
import type {
@@ -426,6 +426,11 @@ export function createListenerMiddleware<
426426

427427
const middleware: ListenerMiddleware<S, D, ExtraArgument> =
428428
(api) => (next) => (action) => {
429+
if (!isAction(action)) {
430+
// this means that the listeners can't react to anything that doesn't look like an action (plain object with .type property)
431+
// but that matches the typing, so i think that's fine?
432+
return next(action)
433+
}
429434
if (addListener.match(action)) {
430435
return startListening(action.payload)
431436
}

packages/toolkit/src/query/core/buildMiddleware/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { AnyAction, Middleware, ThunkDispatch } from '@reduxjs/toolkit'
2-
import { createAction } from '@reduxjs/toolkit'
2+
import { isAction, createAction } from '@reduxjs/toolkit'
33

44
import type {
55
EndpointDefinitions,
@@ -80,6 +80,9 @@ export function buildMiddleware<
8080

8181
return (next) => {
8282
return (action) => {
83+
if (!isAction(action)) {
84+
return next(action)
85+
}
8386
if (!initialized) {
8487
initialized = true
8588
// dispatch before any other action

packages/toolkit/src/query/core/buildMiddleware/types.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,13 @@ export type SubMiddlewareBuilder = (
7171
ThunkDispatch<any, any, AnyAction>
7272
>
7373

74-
export type ApiMiddlewareInternalHandler<ReturnType = void> = (
74+
type MwNext = Parameters<ReturnType<Middleware>>[0]
75+
76+
export type ApiMiddlewareInternalHandler<Return = void> = (
7577
action: AnyAction,
76-
mwApi: SubMiddlewareApi & { next: Dispatch<AnyAction> },
78+
mwApi: SubMiddlewareApi & { next: MwNext },
7779
prevState: RootState<EndpointDefinitions, string, string>
78-
) => ReturnType
80+
) => Return
7981

8082
export type InternalHandlerBuilder<ReturnType = void> = (
8183
input: BuildSubMiddlewareInput

packages/toolkit/src/serializableStateInvariantMiddleware.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import isPlainObject from './isPlainObject'
22
import type { Middleware } from 'redux'
33
import { getTimeMeasureUtils } from './utils'
4+
import { isAction } from './createAction'
45

56
/**
67
* Returns true if the passed value is "plain", i.e. a value that is either
@@ -207,6 +208,10 @@ export function createSerializableStateInvariantMiddleware(
207208
!disableCache && WeakSet ? new WeakSet() : undefined
208209

209210
return (storeAPI) => (next) => (action) => {
211+
if (!isAction(action)) {
212+
return next(action)
213+
}
214+
210215
const result = next(action)
211216

212217
const measureUtils = getTimeMeasureUtils(
@@ -216,7 +221,10 @@ export function createSerializableStateInvariantMiddleware(
216221

217222
if (
218223
!ignoreActions &&
219-
!(ignoredActions.length && ignoredActions.indexOf(action.type) !== -1)
224+
!(
225+
ignoredActions.length &&
226+
ignoredActions.indexOf(action.type as any) !== -1
227+
)
220228
) {
221229
measureUtils.measureTime(() => {
222230
const foundActionNonSerializableValue = findNonSerializableValue(

packages/toolkit/src/tests/immutableStateInvariantMiddleware.test.ts

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type {
33
MiddlewareAPI,
44
Dispatch,
55
ImmutableStateInvariantMiddlewareOptions,
6+
Middleware,
67
} from '@reduxjs/toolkit'
78
import {
89
createImmutableStateInvariantMiddleware,
@@ -16,6 +17,8 @@ import {
1617
getLog,
1718
} from 'console-testing-library/pure'
1819

20+
type MWNext = Parameters<ReturnType<Middleware>>[0]
21+
1922
describe('createImmutableStateInvariantMiddleware', () => {
2023
let state: { foo: { bar: number[]; baz: string } }
2124
const getState: Store['getState'] = () => state
@@ -31,17 +34,16 @@ describe('createImmutableStateInvariantMiddleware', () => {
3134
})
3235

3336
it('sends the action through the middleware chain', () => {
34-
const next: Dispatch = (action) => ({ ...action, returned: true })
35-
const dispatch = middleware()(next)
37+
const next: MWNext = vi.fn()
38+
middleware()(next)
3639

37-
expect(dispatch({ type: 'SOME_ACTION' })).toEqual({
40+
expect(next).toHaveBeenCalledWith({
3841
type: 'SOME_ACTION',
39-
returned: true,
4042
})
4143
})
4244

4345
it('throws if mutating inside the dispatch', () => {
44-
const next: Dispatch = (action) => {
46+
const next: MWNext = (action) => {
4547
state.foo.bar.push(5)
4648
return action
4749
}
@@ -54,7 +56,7 @@ describe('createImmutableStateInvariantMiddleware', () => {
5456
})
5557

5658
it('throws if mutating between dispatches', () => {
57-
const next: Dispatch = (action) => action
59+
const next: MWNext = (action) => action
5860

5961
const dispatch = middleware()(next)
6062

@@ -66,7 +68,7 @@ describe('createImmutableStateInvariantMiddleware', () => {
6668
})
6769

6870
it('does not throw if not mutating inside the dispatch', () => {
69-
const next: Dispatch = (action) => {
71+
const next: MWNext = (action) => {
7072
state = { ...state, foo: { ...state.foo, baz: 'changed!' } }
7173
return action
7274
}
@@ -79,7 +81,7 @@ describe('createImmutableStateInvariantMiddleware', () => {
7981
})
8082

8183
it('does not throw if not mutating between dispatches', () => {
82-
const next: Dispatch = (action) => action
84+
const next: MWNext = (action) => action
8385

8486
const dispatch = middleware()(next)
8587

@@ -91,7 +93,7 @@ describe('createImmutableStateInvariantMiddleware', () => {
9193
})
9294

9395
it('works correctly with circular references', () => {
94-
const next: Dispatch = (action) => action
96+
const next: MWNext = (action) => action
9597

9698
const dispatch = middleware()(next)
9799

@@ -107,7 +109,7 @@ describe('createImmutableStateInvariantMiddleware', () => {
107109

108110
it('respects "isImmutable" option', function () {
109111
const isImmutable = (value: any) => true
110-
const next: Dispatch = (action) => {
112+
const next: MWNext = (action) => {
111113
state.foo.bar.push(5)
112114
return action
113115
}
@@ -120,7 +122,7 @@ describe('createImmutableStateInvariantMiddleware', () => {
120122
})
121123

122124
it('respects "ignoredPaths" option', () => {
123-
const next: Dispatch = (action) => {
125+
const next: MWNext = (action) => {
124126
state.foo.bar.push(5)
125127
return action
126128
}
@@ -139,7 +141,7 @@ describe('createImmutableStateInvariantMiddleware', () => {
139141
})
140142

141143
it('alias "ignore" to "ignoredPath" and respects option', () => {
142-
const next: Dispatch = (action) => {
144+
const next: MWNext = (action) => {
143145
state.foo.bar.push(5)
144146
return action
145147
}
@@ -154,7 +156,7 @@ describe('createImmutableStateInvariantMiddleware', () => {
154156
it('Should print a warning if execution takes too long', () => {
155157
state.foo.bar = new Array(10000).fill({ value: 'more' })
156158

157-
const next: Dispatch = (action) => action
159+
const next: MWNext = (action) => action
158160

159161
const dispatch = middleware({ warnAfter: 4 })(next)
160162

@@ -170,7 +172,7 @@ describe('createImmutableStateInvariantMiddleware', () => {
170172
})
171173

172174
it('Should not print a warning if "next" takes too long', () => {
173-
const next: Dispatch = (action) => {
175+
const next: MWNext = (action) => {
174176
const started = Date.now()
175177
while (Date.now() - started < 8) {}
176178
return action

0 commit comments

Comments
 (0)