UNPKG

clean-redux

Version:

Utilities for implementing clean architecture using Redux

150 lines (137 loc) 5.24 kB
import "minimal-polyfills/Object.fromEntries"; import { Polyfill as WeakMap } from "minimal-polyfills/WeakMap"; import type { Param0 } from "tsafe"; import { objectKeys } from "tsafe/objectKeys"; import type { ThunkAction, AnyAction } from "@reduxjs/toolkit"; export type ThunkToAutoDispatchThunk<Thunk extends (params: any) => ThunkAction<any, any, any, any>> = ( params: Param0<Thunk>, ) => ReturnType<Thunk> extends ThunkAction<infer R, any, any, any> ? R : never; /** NOTE: Always returns the same ref for a given dispatch, no need to useMemo */ export function thunkToAutoDispatchThunk< Thunk extends (params: any) => ThunkAction<any, any, any, AnyAction>, >(params: { thunk: Thunk; dispatch: ( thunkAction: ThunkAction< ReturnType<Thunk> extends ThunkAction<infer RtnType, any, any, AnyAction> ? RtnType : never, ReturnType<Thunk> extends ThunkAction<any, infer State, any, AnyAction> ? State : never, ReturnType<Thunk> extends ThunkAction<any, any, infer ExtraThunkArg, AnyAction> ? ExtraThunkArg : never, AnyAction >, ) => ReturnType<Thunk> extends ThunkAction<infer RtnType, any, any, AnyAction> ? RtnType : never; }): ThunkToAutoDispatchThunk<Thunk> { const { dispatch, thunk } = params; return (params: Param0<Thunk>) => dispatch(thunk(params)) as any; } export type ThunksToAutoDispatchThunks< Thunks extends Record<string, (params: any) => ThunkAction<any, any, any, AnyAction>>, > = { [Key in keyof Thunks]: ThunkToAutoDispatchThunk<Thunks[Key]> }; export function thunksToAutoDispatchThunks< Thunks extends Record<string, (params: any) => ThunkAction<any, any, any, any>>, >(params: { thunks: Thunks; dispatch: ( thunkAction: ThunkAction< ReturnType<Thunks[keyof Thunks]> extends ThunkAction<infer RtnType, any, any, AnyAction> ? RtnType : never, ReturnType<Thunks[keyof Thunks]> extends ThunkAction<any, infer State, any, AnyAction> ? State : never, ReturnType<Thunks[keyof Thunks]> extends ThunkAction< any, any, infer ExtraThunkArg, AnyAction > ? ExtraThunkArg : never, AnyAction >, ) => ReturnType<Thunks[keyof Thunks]> extends ThunkAction<infer RtnType, any, any, AnyAction> ? RtnType : never; }): ThunksToAutoDispatchThunks<Thunks> { const { dispatch, thunks } = params; return Object.fromEntries( objectKeys(thunks).map(name => [ name, thunkToAutoDispatchThunk({ "thunk": thunks[name], dispatch }), ]), ) as any; } const wordId = "Thunks"; export function usecasesToAutoDispatchThunks< Usecase extends { name: string; thunks: Record<string, (params: any) => ThunkAction<any, any, any, any>>; }, >( usecases: readonly Usecase[], ): { getAutoDispatchThunks: ( dispatch: ( thunkAction: ThunkAction< ReturnType<Usecase["thunks"][keyof Usecase["thunks"]]> extends ThunkAction< infer RtnType, any, any, AnyAction > ? RtnType : never, ReturnType<Usecase["thunks"][keyof Usecase["thunks"]]> extends ThunkAction< any, infer State, any, AnyAction > ? State : never, ReturnType<Usecase["thunks"][keyof Usecase["thunks"]]> extends ThunkAction< any, any, infer ExtraThunkArg, AnyAction > ? ExtraThunkArg : never, AnyAction >, ) => ReturnType<Usecase["thunks"][keyof Usecase["thunks"]]> extends ThunkAction< infer RtnType, any, any, AnyAction > ? RtnType : never, ) => { [Key in `${Usecase["name"]}${typeof wordId}`]: ThunksToAutoDispatchThunks< Extract< Usecase, { name: Key extends `${infer Name}${typeof wordId}` ? Name : never } >["thunks"] >; }; } { const autoDispatchThunksByDispatch = new WeakMap<Function, any>(); return { "getAutoDispatchThunks": dispatch => { let autoDispatchThunks = autoDispatchThunksByDispatch.get(dispatch); if (autoDispatchThunks !== undefined) { return autoDispatchThunks; } autoDispatchThunks = Object.fromEntries( usecases.map(({ name, thunks }) => [ `${name}${wordId}`, thunksToAutoDispatchThunks({ thunks, dispatch }), ]), ) as any; autoDispatchThunksByDispatch.set(dispatch, autoDispatchThunks); return autoDispatchThunks; }, }; }