From 847264253ec4efd0227428e852b3a2eb12fb07d7 Mon Sep 17 00:00:00 2001 From: Ben Durrant Date: Mon, 20 Nov 2023 15:28:59 +0000 Subject: [PATCH 1/3] Add isAction type predicate --- src/index.ts | 2 ++ src/utils/isAction.ts | 10 ++++++++++ src/utils/isPlainObject.ts | 2 +- 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 src/utils/isAction.ts diff --git a/src/index.ts b/src/index.ts index 8bdd6c4e7c..26881bebfa 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,6 +4,7 @@ import combineReducers from './combineReducers' import bindActionCreators from './bindActionCreators' import applyMiddleware from './applyMiddleware' import compose from './compose' +import isAction from './utils/isAction' import __DO_NOT_USE__ActionTypes from './utils/actionTypes' // types @@ -42,5 +43,6 @@ export { bindActionCreators, applyMiddleware, compose, + isAction, __DO_NOT_USE__ActionTypes } diff --git a/src/utils/isAction.ts b/src/utils/isAction.ts new file mode 100644 index 0000000000..878f4a8096 --- /dev/null +++ b/src/utils/isAction.ts @@ -0,0 +1,10 @@ +import { Action } from '../types/actions' +import isPlainObject from './isPlainObject' + +export default function isAction(action: unknown): action is Action { + return ( + isPlainObject(action) && + 'type' in action && + typeof (action as Record<'type', unknown>).type === 'string' + ) +} diff --git a/src/utils/isPlainObject.ts b/src/utils/isPlainObject.ts index 9d1956328a..de6a260af0 100644 --- a/src/utils/isPlainObject.ts +++ b/src/utils/isPlainObject.ts @@ -2,7 +2,7 @@ * @param obj The object to inspect. * @returns True if the argument appears to be a plain object. */ -export default function isPlainObject(obj: any): boolean { +export default function isPlainObject(obj: any): obj is object { if (typeof obj !== 'object' || obj === null) return false let proto = obj From 7859c01b4c922e1b7b171561f91a1d605a7f8d94 Mon Sep 17 00:00:00 2001 From: Ben Durrant Date: Mon, 20 Nov 2023 15:33:16 +0000 Subject: [PATCH 2/3] tests --- test/utils/isAction.spec.ts | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 test/utils/isAction.spec.ts diff --git a/test/utils/isAction.spec.ts b/test/utils/isAction.spec.ts new file mode 100644 index 0000000000..029189f265 --- /dev/null +++ b/test/utils/isAction.spec.ts @@ -0,0 +1,23 @@ +import { isAction } from 'redux' + +describe('isAction', () => { + it('should only return true for plain objects with a string type property', () => { + const actionCreator = () => ({ type: 'anAction' }) + class Action { + type = 'totally an action' + } + const testCases: [action: unknown, expected: boolean][] = [ + [{ type: 'an action' }, true], + [{ type: 'more props', extra: true }, true], + [{ type: 0 }, false], + [actionCreator(), true], + [actionCreator, false], + [Promise.resolve({ type: 'an action' }), false], + [new Action(), false], + ['a string', false] + ] + for (const [action, expected] of testCases) { + expect(isAction(action)).toBe(expected) + } + }) +}) From d44a074b6ac1f6626d11fdeffea782cb52e6007a Mon Sep 17 00:00:00 2001 From: Ben Durrant Date: Mon, 20 Nov 2023 15:33:47 +0000 Subject: [PATCH 3/3] use internal import --- test/utils/isAction.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/utils/isAction.spec.ts b/test/utils/isAction.spec.ts index 029189f265..165495e902 100644 --- a/test/utils/isAction.spec.ts +++ b/test/utils/isAction.spec.ts @@ -1,4 +1,4 @@ -import { isAction } from 'redux' +import isAction from '@internal/utils/isAction' describe('isAction', () => { it('should only return true for plain objects with a string type property', () => {