redux-actions-promise-wrapper
Version:
Type safe wrapper for redux actions (compatible with redux-saga)
251 lines (238 loc) • 6.21 kB
text/typescript
import { call, put as sagaPut } from 'redux-saga/effects';
import { Action, GReturnable, IIReturn, rpResolve } from './types';
/**
* Automatically resolves promise of Action.promise
* @param innerSaga - Generator Function for redux-saga
*/
const promiseHandler = <T extends string, P, R>(
innerSaga: (args?: P) => IterableIterator<R>
) =>
function* innerFunction(action: Action<T, P, IIReturn<R>>) {
try {
const data: IIReturn<R> = yield call(innerSaga, action.payload);
if (action.resolve) {
action.resolve(data);
}
} catch (e) {
if (action.reject) {
action.reject(e);
}
}
};
/**
* Action Creator
* @param type Action Type
*/
const actionCreator = <T, P, R>(type: T) => (payload?: P) => {
let resolve: (value: IIReturn<R>) => void = () => { };
let reject: (reason?: any) => void = () => { };
const promise = new Promise<IIReturn<R>>((pResolve, pReject) => {
resolve = pResolve;
reject = pReject;
});
return {
type,
payload,
promise,
resolve,
reject
};
};
export { rpResolve };
/**
* Extract Actions to be used in reducer
* @example
* type Actions = ExtractActions<loginAction | logoutAction>
*/
export type ExtractActions<Base> = {
[Key in keyof Base]: ActionUnion<Base[Key]>
}[keyof Base];
export type ActionUnion<A> = {
[Key in keyof A]: A[Key] extends (...args: any[]) => any
? ReturnType<A[Key]> extends IterableIterator<any>
? never
: ReturnType<A[Key]>
: never
}[keyof A];
/**
* Generator return value
* @param t any
*/
export function reg<T>(t: T) {
return t as GReturnable<T>;
}
// Helper types
type invokeSignature<T extends string, P, R> = P extends undefined
? {
(): Action<T, P, IIReturn<R>>;
}
: P extends boolean ? {
(payload: boolean): Action<T, P, IIReturn<R>>;
} : {
(payload: P): Action<T, P, IIReturn<R>>;
};
type handleAction<P, R, H> =
| (P extends undefined
? () => IterableIterator<R>
: (args: P) => IterableIterator<R>)
| H;
type actionCreator<T extends string, P, R> = P extends undefined
? () => Action<T, P, IIReturn<R>>
: P extends boolean
? (payload: boolean) => Action<T, P, IIReturn<R>>
: (payload: P) => Action<T, P, IIReturn<R>>;
type actionHandler<T extends string, P, H, R> = H extends undefined
? undefined
: (action: Action<T, P, IIReturn<R>>) => IterableIterator<any>;
export function createAction<T extends string>(
type: T
): {
(): {
(): Action<T>;
TRIGGER: T;
trigger: () => Action<T>;
};
<P = undefined, H = undefined, R = undefined>(
handleAction?: handleAction<P, R, H>
): {
TRIGGER: T;
trigger: actionCreator<T, P, R>;
handleTrigger: actionHandler<T, P, H, R>;
} & invokeSignature<T, P, R>;
};
export function createAction<T extends string, ST extends string>(
type: T,
successType: ST
): {
(): {
(): Action<T>;
TRIGGER: T;
trigger: () => Action<T>;
SUCCESS: ST;
success: () => Action<ST>;
};
<
P = undefined,
SP = undefined,
H = undefined,
R = undefined,
SH = undefined,
SR = undefined
>(
handleAction?: handleAction<P, R, H>,
handleSuccess?: handleAction<SP, SR, SH>
): {
TRIGGER: T;
trigger: actionCreator<T, P, R>;
handleTrigger: actionHandler<T, P, H, R>;
SUCCESS: ST;
success: actionCreator<ST, SP, SR>;
handleSuccess: actionHandler<ST, SP, SH, SR>;
} & invokeSignature<T, P, R>;
};
export function createAction<
T extends string,
ST extends string,
FT extends string
>(
type: T,
successType: ST,
failureType: FT
): {
(): {
(): Action<T>;
TRIGGER: T;
trigger: () => Action<T>;
SUCCESS: ST;
success: () => Action<ST>;
FAILURE: FT;
failure: () => Action<FT>;
};
<
P = undefined,
SP = undefined,
FP = undefined,
H = undefined,
R = undefined,
SH = undefined,
SR = undefined,
FH = undefined,
FR = undefined
>(
handleAction?: handleAction<P, R, H>,
handleSuccess?: handleAction<SP, SR, SH>,
handleFailure?: handleAction<FP, FR, FH>
): {
TRIGGER: T;
trigger: actionCreator<T, P, R>;
handleTrigger: actionHandler<T, P, H, R>;
SUCCESS: ST;
success: actionCreator<ST, SP, SR>;
handleSuccess: actionHandler<ST, SP, SH, SR>;
FAILURE: FT;
failure: actionCreator<FT, FP, FR>;
handleFailure: actionHandler<FT, FP, FH, FR>;
} & invokeSignature<T, P, R>;
};
export function createAction<
T extends string,
ST extends string,
FT extends string
>(type: T, successType?: ST, failureType?: FT) {
function actionPayload<P, R, SP, SR, FP, FR>(
handleTrigger?: (args?: P) => IterableIterator<R>,
handleSuccess?: (args?: SP) => IterableIterator<SR>,
handleFailure?: (args?: FP) => IterableIterator<FR>
) {
// handler return
const routine = (payload?: P) => {
let resolve: (value: IIReturn<R>) => void = () => { };
let reject: (reason?: any) => void = () => { };
const promise = new Promise<IIReturn<R>>((pResolve, pReject) => {
resolve = pResolve;
reject = pReject;
});
return {
type,
payload,
promise,
resolve,
reject
};
};
routine.TRIGGER = type;
routine.trigger = actionCreator<T, P, R>(type);
if (handleTrigger) {
routine.handleTrigger = promiseHandler<T, P, R>(handleTrigger);
}
if (successType) {
routine.SUCCESS = successType;
routine.success = actionCreator<ST, SP, SR>(successType);
if (handleSuccess) {
routine.handleSuccess = promiseHandler<ST, SP, SR>(handleSuccess);
}
}
if (failureType) {
routine.FAILURE = failureType;
routine.failure = actionCreator<FT, FP, FR>(failureType);
if (handleFailure) {
routine.handleFailure = promiseHandler<FT, FP, FR>(handleFailure);
}
}
return routine;
}
return actionPayload;
}
/**
* transform return type of redux-saga's put to any
* @param action Action
*/
interface Iput {
test: any;
}
export const put = (action: Ia) => (sagaPut(action) as unknown) as Iput;
interface Ia {
type: string;
payload?: any;
[key: string]: any;
}