// SPDX-License-Identifier: MIT // Copyright (c) 2022 The Pybricks Authors import { AnyAction } from 'redux'; /** A function that creates action objects. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any type ActionCreationFunction = (...args: any[]) => A; /** A function that performs type discrimination on an action. */ type MatchFunction = (action: AnyAction) => action is A; /** The extra members that are attached to a function by createAction(). */ type MatchableExtensions, A extends AnyAction> = { /** * This should not usually be used directly. It allows Matchable action * functions to be passed directly to redux saga effects as an action pattern. */ toString(): ReturnType['type']; /** * Type guard to ensure an action matches this type. */ matches: MatchFunction>; /** * Type guard creation function with addition filtering. * * This is useful for creating a guard function to pass to redux saga * effects. * * @example const action = yield* take(someAction.when((a) => a.property === value)); * * @param predicate An predicate to filter actions. */ when(predicate: (action: ReturnType) => boolean): MatchFunction>; }; /** An action creation function that includes MatchableExtensions. */ type Matchable, A extends AnyAction> = F & MatchableExtensions; /** * Adds additional members to an action creation function. * * @param actionCreator The action creation function. * @returns actionCreator with type property and match method added. */ export function createAction, A extends AnyAction>( actionCreator: F, ): Matchable { // create a default action so we can get the type string. const type = actionCreator().type; function matches(action: AnyAction): action is ReturnType { return action.type === type; } function when( predicate: (action: ReturnType) => boolean, ): MatchFunction> { return (a: AnyAction): a is ReturnType => { if (!matches(a)) { return false; } return predicate(a); }; } return Object.assign(actionCreator, >{ toString: () => type, matches, when, }); }