@reduxjs/toolkit
Version:
The official, opinionated, batteries-included toolset for efficient Redux development
209 lines (179 loc) • 6.04 kB
text/typescript
import { Action } from 'redux'
import { IsUnknownOrNonInferrable } from './tsHelpers'
/**
* An action with a string type and an associated payload. This is the
* type of action returned by `createAction()` action creators.
*
* @template P The type of the action's payload.
* @template T the type used for the action type.
* @template M The type of the action's meta (optional)
* @template E The type of the action's error (optional)
*/
export type PayloadAction<
P = void,
T extends string = string,
M = never,
E = never
> = WithOptional<M, E, WithPayload<P, Action<T>>>
export type PrepareAction<P> =
| ((...args: any[]) => { payload: P })
| ((...args: any[]) => { payload: P; meta: any })
| ((...args: any[]) => { payload: P; error: any })
| ((...args: any[]) => { payload: P; meta: any; error: any })
export type ActionCreatorWithPreparedPayload<
PA extends PrepareAction<any> | void,
T extends string = string
> = PA extends PrepareAction<infer P>
? WithTypePropertyAndMatch<
(
...args: Parameters<PA>
) => PayloadAction<P, T, MetaOrNever<PA>, ErrorOrNever<PA>>,
T,
P,
MetaOrNever<PA>,
ErrorOrNever<PA>
>
: void
export type ActionCreatorWithOptionalPayload<
P,
T extends string = string
> = WithTypePropertyAndMatch<
{
(payload?: undefined): PayloadAction<undefined, T>
<PT extends Diff<P, undefined>>(payload?: PT): PayloadAction<PT, T>
},
T,
P | undefined
>
export type ActionCreatorWithoutPayload<
T extends string = string
> = WithTypePropertyAndMatch<() => PayloadAction<undefined, T>, T, undefined>
export type ActionCreatorWithPayload<
P,
T extends string = string
> = WithTypePropertyAndMatch<
IsUnknownOrNonInferrable<
P,
// TS < 3.5 infers non-inferrable types to {}, which does not take `null`. This enforces `undefined` instead.
<PT extends unknown>(payload: PT) => PayloadAction<PT, T>,
// default behaviour
<PT extends P>(payload: PT) => PayloadAction<PT, T>
>,
T,
P
>
/**
* An action creator that produces actions with a `payload` attribute.
*/
export type PayloadActionCreator<
P = void,
T extends string = string,
PA extends PrepareAction<P> | void = void
> = IfPrepareActionMethodProvided<
PA,
ActionCreatorWithPreparedPayload<PA, T>,
// else
IfMaybeUndefined<
P,
ActionCreatorWithOptionalPayload<P, T>,
// else
IfVoid<
P,
ActionCreatorWithoutPayload<T>,
// else
ActionCreatorWithPayload<P, T>
>
>
>
/**
* A utility function to create an action creator for the given action type
* string. The action creator accepts a single argument, which will be included
* in the action object as a field called payload. The action creator function
* will also have its toString() overriden so that it returns the action type,
* allowing it to be used in reducer logic that is looking for that action type.
*
* @param type The action type to use for created actions.
* @param prepare (optional) a method that takes any number of arguments and returns { payload } or { payload, meta }.
* If this is given, the resulting action creator will pass it's arguments to this method to calculate payload & meta.
*/
export function createAction<P = void, T extends string = string>(
type: T
): PayloadActionCreator<P, T>
export function createAction<
PA extends PrepareAction<any>,
T extends string = string
>(
type: T,
prepareAction: PA
): PayloadActionCreator<ReturnType<PA>['payload'], T, PA>
export function createAction(type: string, prepareAction?: Function) {
function actionCreator(...args: any[]) {
if (prepareAction) {
let prepared = prepareAction(...args)
if (!prepared) {
throw new Error('prepareAction did not return an object')
}
return {
type,
payload: prepared.payload,
...('meta' in prepared && { meta: prepared.meta }),
...('error' in prepared && { error: prepared.error })
}
}
return { type, payload: args[0] }
}
actionCreator.toString = () => `${type}`
actionCreator.type = type
actionCreator.match = (action: Action<unknown>): action is PayloadAction =>
action.type === type
return actionCreator
}
/**
* Returns the action type of the actions created by the passed
* `createAction()`-generated action creator (arbitrary action creators
* are not supported).
*
* @param action The action creator whose action type to get.
* @returns The action type used by the action creator.
*/
export function getType<T extends string>(
actionCreator: PayloadActionCreator<any, T>
): T {
return `${actionCreator}` as T
}
// helper types for more readable typings
type Diff<T, U> = T extends U ? never : T
type WithPayload<P, T> = T & { payload: P }
type WithOptional<M, E, T> = T &
([M] extends [never] ? {} : { meta: M }) &
([E] extends [never] ? {} : { error: E })
type WithTypeProperty<MergeIn, T extends string> = {
type: T
} & MergeIn
type WithMatch<MergeIn, T extends string, P, M = never, E = never> = {
match(action: Action<unknown>): action is PayloadAction<P, T, M, E>
} & MergeIn
type WithTypePropertyAndMatch<
MergeIn,
T extends string,
P,
M = never,
E = never
> = WithTypeProperty<WithMatch<MergeIn, T, P, M, E>, T>
type IfPrepareActionMethodProvided<
PA extends PrepareAction<any> | void,
True,
False
> = PA extends (...args: any[]) => any ? True : False
type MetaOrNever<PA extends PrepareAction<any>> = ReturnType<PA> extends {
meta: infer M
}
? M
: never
type ErrorOrNever<PA extends PrepareAction<any>> = ReturnType<PA> extends {
error: infer E
}
? E
: never
type IfMaybeUndefined<P, True, False> = [undefined] extends [P] ? True : False
type IfVoid<P, True, False> = [void] extends [P] ? True : False