@equinor/fusion-observable
Version:
92 lines (91 loc) • 5.88 kB
TypeScript
import type { Draft } from 'immer';
import type { TypeGuard } from './types/ts-helpers';
import type { Action, ActionType, AnyAction, ExtractAction } from './types/actions';
import type { ReducerWithInitialState } from './types/reducers';
type NotFunction<T = unknown> = T extends Function ? never : T;
export type ActionMatcherDescription<S, A extends AnyAction> = {
matcher: TypeGuard<A> | ((action: A) => boolean);
reducer: CaseReducer<S, NoInfer<A>>;
};
type ActionMatcherDescriptionCollection<S> = Array<ActionMatcherDescription<S, any>>;
interface TypedActionCreator<Type extends string> {
(...args: any[]): Action<Type>;
type: Type;
}
type CaseReducer<S = unknown, A extends Action = AnyAction> = (state: Draft<S>, action: A) => S | void | Draft<S>;
type CaseReducers<S, AS extends Record<string, Action>> = {
[T in keyof AS]: AS[T] extends Action ? CaseReducer<S, AS[T]> : void;
};
/**
* A utility function that allows defining a reducer as a mapping from action
* type to *case reducer* functions that handle these action types. The
* reducer's initial state is passed as the first argument.
*
* @remarks
* The body of every case reducer is implicitly wrapped with a call to
* `produce()` from the [immer](https://github.com/mweststrate/immer) library.
* This means that rather than returning a new state object, you can also
* mutate the passed-in state object directly; these mutations will then be
* automatically and efficiently translated into copies, giving you both
* convenience and immutability.
*
* @overloadSummary
* This overload accepts a callback function that receives a `builder` object as its argument.
* That builder provides `addCase`, `addMatcher` and `addDefaultCase` functions that may be
* called to define what actions this reducer will handle.
*
* @param initialState - `State | (() => State)`: The initial state that should be used when the reducer is called the first time. This may also be a "lazy initializer" function, which should return an initial state value when called. This will be used whenever the reducer is called with `undefined` as its state value, and is primarily useful for cases like reading initial state from `localStorage`.
* @param builderCallback - `(builder: Builder) => void` A callback that receives a *builder* object to define
* case reducers via calls to `builder.addCase(actionCreatorOrType, reducer)`.
*
* @public
*/
export declare function createReducer<S extends NotFunction, A extends Action = AnyAction>(initialState: S | (() => S), builderCallback: (builder: ActionReducerMapBuilder<S, A>) => void): ReducerWithInitialState<S, A>;
/**
* A builder for an action <-> reducer map.
*
* @public
*/
export interface ActionReducerMapBuilder<State, Actions extends AnyAction = AnyAction> {
/**
* Adds a case reducer to handle a single exact action type.
* @remarks
* All calls to `builder.addCase` must come before any calls to `builder.addMatcher` or `builder.addDefaultCase`.
* @param actionCreator - Either a plain action type string, or an action creator generated by [`createAction`](./createAction) that can be used to determine the action type.
* @param reducer - The actual case reducer function.
*/
addCase<ActionCreator extends TypedActionCreator<ActionType<Actions>>>(actionCreator: ActionCreator, reducer: CaseReducer<State, ReturnType<ActionCreator>>): ActionReducerMapBuilder<State, Actions>;
/**
* Adds a case reducer to handle a single exact action type.
* @remarks
* All calls to `builder.addCase` must come before any calls to `builder.addMatcher` or `builder.addDefaultCase`.
* @param actionCreator - Either a plain action type string, or an action creator generated by [`createAction`](./createAction) that can be used to determine the action type.
* @param reducer - The actual case reducer function.
*/
addCase<Type extends Actions['type']>(type: Type, reducer: CaseReducer<State, ExtractAction<Actions, Type>>): ActionReducerMapBuilder<State, Actions>;
/**
* Allows you to match your incoming actions against your own filter function instead of only the `action.type` property.
* @remarks
* If multiple matcher reducers match, all of them will be executed in the order
* they were defined in - even if a case reducer already matched.
* All calls to `builder.addMatcher` must come after any calls to `builder.addCase` and before any calls to `builder.addDefaultCase`.
* @param matcher - A matcher function. In TypeScript, this should be a [type predicate](https://www.typescriptlang.org/docs/handbook/advanced-types.html#using-type-predicates)
* function
* @param reducer - The actual case reducer function.
*
*/
addMatcher<TAction extends Actions = Actions>(matcher: (action: Actions) => action is TAction, reducer: CaseReducer<State, TAction extends AnyAction ? TAction : TAction & AnyAction>): Omit<ActionReducerMapBuilder<State, Actions>, 'addCase'>;
addMatcher<TAction extends Actions = Actions>(matcher: (action: Actions) => boolean, reducer: CaseReducer<State, TAction extends AnyAction ? TAction : TAction & AnyAction>): Omit<ActionReducerMapBuilder<State, Actions>, 'addCase'>;
/**
* Adds a "default case" reducer that is executed if no case reducer and no matcher
* reducer was executed for this action.
* @param reducer - The fallback "default case" reducer function.
*/
addDefaultCase(reducer: CaseReducer<State, AnyAction>): void;
}
export declare function executeReducerBuilderCallback<TState, TAction extends AnyAction>(builderCallback: (builder: ActionReducerMapBuilder<TState, TAction>) => void): [
CaseReducers<TState, Record<string, TAction>>,
ActionMatcherDescriptionCollection<TState>,
CaseReducer<TState, TAction> | undefined
];
export {};