Last active
November 11, 2021 17:55
-
-
Save realiarthur/4aa1e26109bdd9d8b11d0b982d6aab87 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Action Creator | |
type ActionCreator<P> = <A extends keyof P, TParams extends any[]>( | |
type: A, | |
prepare?: P[A] extends void ? undefined : (...args: TParams) => P[A], | |
) => ( | |
...args: P[A] extends void ? [] : TParams extends undefined[] ? [payload: P[A]] : TParams | |
) => Action<A, P> | |
/** | |
* Function return action create helper that based on actions payloads interface. | |
* | |
* @param {string} type - Action type | |
* @param {(...args: TPrepareParams) => P[A]=} prepare - Prepare function for create action. | |
* If undefined and P[A] is not void - it forward payload as is. | |
*/ | |
export const getActionCreator = | |
<P>(): ActionCreator<P> => | |
(type, prepare) => | |
(...args) => ({ | |
type, | |
payload: (prepare ? prepare(...(args as any)) : args[0]) as any, | |
}) | |
// Reducer Creator | |
type Reducers<S, P> = { | |
[Property in keyof P]: Reducer<S, P[Property]> | |
} | |
type ReducerCreator<P, S> = { | |
_reducers: Reducers<S, P> | |
add: <A extends keyof P>(type: A, reducer: Reducer<S, P[A]>) => void | |
create: () => (state: S | undefined, { type, payload }) => S | |
} | |
/** | |
* Function return reducer create helper that based on actions payloads interface. | |
* | |
* @param initialState - Initial State Slice | |
*/ | |
export const getReducerCreator = <S, P>( | |
initialState: S, | |
): ReducerCreator<P, S> => ({ | |
_reducers: Object.create({}), | |
add: function (type, reducer) { | |
this._reducers[type] = reducer | |
}, | |
create: function () { | |
return (state = initialState as S, { type, payload }): S => { | |
return this._reducers.hasOwnProperty(type) ? this._reducers[type](state, payload) : state | |
} | |
}, | |
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
interface SimpleAction<A> { | |
type: A | |
} | |
interface ActionWithPayload<A, P> extends SimpleAction<A> { | |
payload: P | |
} | |
type Action<A extends keyof P, P> = TPayloads[A] extends void ? SimpleAction<A> : ActionWithPayload<A, P[A]> | |
interface SimpleReducer<S> { | |
(state: S): S | |
} | |
interface ReducerWithPayload<S, P> { | |
(state: S, payload: P): S | |
} | |
type Reducer<S, P = void> = P extends void ? SimpleReducer<S> : ReducerWithPayload<S, P> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { getActionCreator, getReducerCreator } from './typed-redux-helpers' | |
enum ACTIONS { | |
fetchStart = 'fetchStart', | |
fetchSuccess = 'fetchSuccess', | |
} | |
interface Payloads { | |
[ACTIONS.fetchStart]: void | |
[ACTIONS.fetchSuccess]: number | |
} | |
// Actions | |
export const createAction = getActionCreator<Payloads>() | |
export const fetchStart = createAction(ACTIONS.fetchStart) | |
export const fetchSuccess = createAction(ACTIONS.fetchSuccess, (text: string) => Number.parseInt(text)) | |
// Reducer | |
const initialState = { | |
loading: false, | |
data: 0, | |
} | |
type StateSlice = typeof initialState | |
const reducer = getReducerCreator<StateSlice, Payloads>(initialState) | |
reducer.add(ACTIONS.fetchStart, state => ({ ...state, loading: true })) | |
reducer.add(ACTIONS.fetchSuccess, (state, payload) => ({ ...state, loading: false, data: payload })) | |
export default reducer.create() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment