Skip to content

Commit 85d7f27

Browse files
authored
Merge pull request #4620 from reduxjs/is-action-predicate
Add isAction type predicate
2 parents 6804e81 + d44a074 commit 85d7f27

File tree

4 files changed

+36
-1
lines changed

4 files changed

+36
-1
lines changed

src/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import combineReducers from './combineReducers'
44
import bindActionCreators from './bindActionCreators'
55
import applyMiddleware from './applyMiddleware'
66
import compose from './compose'
7+
import isAction from './utils/isAction'
78
import __DO_NOT_USE__ActionTypes from './utils/actionTypes'
89

910
// types
@@ -42,5 +43,6 @@ export {
4243
bindActionCreators,
4344
applyMiddleware,
4445
compose,
46+
isAction,
4547
__DO_NOT_USE__ActionTypes
4648
}

src/utils/isAction.ts

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { Action } from '../types/actions'
2+
import isPlainObject from './isPlainObject'
3+
4+
export default function isAction(action: unknown): action is Action<string> {
5+
return (
6+
isPlainObject(action) &&
7+
'type' in action &&
8+
typeof (action as Record<'type', unknown>).type === 'string'
9+
)
10+
}

src/utils/isPlainObject.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* @param obj The object to inspect.
33
* @returns True if the argument appears to be a plain object.
44
*/
5-
export default function isPlainObject(obj: any): boolean {
5+
export default function isPlainObject(obj: any): obj is object {
66
if (typeof obj !== 'object' || obj === null) return false
77

88
let proto = obj

test/utils/isAction.spec.ts

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import isAction from '@internal/utils/isAction'
2+
3+
describe('isAction', () => {
4+
it('should only return true for plain objects with a string type property', () => {
5+
const actionCreator = () => ({ type: 'anAction' })
6+
class Action {
7+
type = 'totally an action'
8+
}
9+
const testCases: [action: unknown, expected: boolean][] = [
10+
[{ type: 'an action' }, true],
11+
[{ type: 'more props', extra: true }, true],
12+
[{ type: 0 }, false],
13+
[actionCreator(), true],
14+
[actionCreator, false],
15+
[Promise.resolve({ type: 'an action' }), false],
16+
[new Action(), false],
17+
['a string', false]
18+
]
19+
for (const [action, expected] of testCases) {
20+
expect(isAction(action)).toBe(expected)
21+
}
22+
})
23+
})

0 commit comments

Comments
 (0)