Skip to content

Instantly share code, notes, and snippets.

@Vovan-VE
Last active July 19, 2021 14:31
Show Gist options
  • Save Vovan-VE/4be25aa16933e2df9cc0f8ce96ec7326 to your computer and use it in GitHub Desktop.
Save Vovan-VE/4be25aa16933e2df9cc0f8ce96ec7326 to your computer and use it in GitHub Desktop.
TypeScript: redux-thunk & redux-promise-middleware together
import { Action } from 'redux';
import { AsyncAction, AsyncFulfilledAction } from './redux-thunk-promise';
import { ApiResult } from 'api/...';
export const FETCH = '.../FETCH';
export const FETCH_PENDING = '.../FETCH_PENDING';
export const FETCH_FULFILLED = '.../FETCH_FULFILLED';
export const FETCH_REJECTED = '.../FETCH_REJECTED';
export type FetchAction = AsyncAction<typeof FETCH, ApiResult>;
export type FetchPendingAction = Action<typeof FETCH_PENDING>;
export type FetchDoneAction = Action<typeof FETCH_FULFILLED> & {
payload: ApiResult;
};
export type FetchFailAction = Action<typeof FETCH_REJECTED> & {
payload: Error;
error: true;
};
export type FooAction =
| FetchAction
| FetchPendingAction
| FetchDoneAction
| FetchFailAction;
import { AppState, ThunkAction } from './example.store';
import { FETCH, FetchAction } from './example.action-types';
const fetch = (): FetchAction => ({
type: FETCH,
payload: api.list(), // `api.list` is `() => Promise<ApiResult>`
});
export const fetchFoo = (): ThunkAction<void> =>
(dispatch, getState) => {
if (...) {
dispatch(fetch()).then(({ value }) => {
// value is ApiResult
});
}
};
import { ApiResult } from 'api/...';
import {
FooAction,
FETCH_PENDING,
FETCH_FULFILLED,
FETCH_REJECTED,
} from './example.action-types.ts';
export interface State {
data: ApiResult | null;
//...
}
const initialState: State = {
data: null,
//...
};
export default (state = initialState, action: FooAction): State => {
switch (action.type) {
case FETCH_PENDING:
return {
...state,
//...
};
case FETCH_FULFILLED:
return {
...state,
data: action.payload,
};
case FETCH_REJECTED:
return {
...state,
//...
};
default:
return state;
}
};
import { Store } from 'redux';
import { AppState } from 'state/reducer';
import {
AppDispatch,
AsyncAction as _AsyncAction,
ThunkAction as _ThunkAction,
} from './redux-thunk-promise';
export type AppStateStore = Store<AppState> & {
dispatch: AppDispatch<AppState, null>;
};
export type AsyncAction<T, R> = _AsyncAction<T, R>;
export type ThunkAction<R> = _ThunkAction<R, AppState>;
// https://github.com/pburtchaell/redux-promise-middleware/issues/253
import { Action } from 'redux';
// ==================================
// redux-promise-middleware extension
// ==================================
declare type AsyncFunction<R = any> = () => Promise<R>;
declare type AsyncPayload<R = any> =
| Promise<R>
| AsyncFunction<R>
| {
promise: Promise<R> | AsyncFunction<R>;
data?: any;
};
export declare interface AsyncAction<T = any, R = any> extends Action<T> {
payload: AsyncPayload<R>;
}
type AsyncActionResult<A> = A extends AsyncAction<any, infer R> ? R : never;
export type AsyncFulfilledAction<A extends AsyncAction, T extends string = string> = Action<T> & {
payload: AsyncActionResult<A>;
};
type FulfilledDispatchResult<A extends AsyncAction> = {
action: AsyncFulfilledAction<A>;
value: AsyncActionResult<A>;
};
export type AsyncDispatchReturns<T> = T extends AsyncAction
? Promise<FulfilledDispatchResult<T>>
: T;
// ==========================
// redux-thunk
// ==========================
export type ThunkDispatchReturns<S, E, A> = A extends ThunkAction<infer R, S, E> ? R : A;
export declare type ThunkAction<R, S, E = null, D = AppDispatch<S, E>> = (
dispatch: D,
getState: () => S,
extraArgument: E,
) => R;
// =========================
export type AppDispatchResult<S, E, A> = AsyncDispatchReturns<ThunkDispatchReturns<S, E, A>>;
export interface AppDispatch<S, E> {
<T extends Action | ThunkAction<any, S, E>>(action: T): AppDispatchResult<S, E, T>;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment