// 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,
});
}