Skip to content

Instantly share code, notes, and snippets.

@mlewando
Created January 11, 2019 22:00
Show Gist options
  • Save mlewando/e578e28e1c0584ce28a1fc3d30a7f377 to your computer and use it in GitHub Desktop.
Save mlewando/e578e28e1c0584ce28a1fc3d30a7f377 to your computer and use it in GitHub Desktop.
Typescript mapped types for redux factory problem
import { createAction, createReducer } from "./redux-helpers";
export type State = Readonly<{
token?: string;
loading: boolean;
loginError?: string;
}>;
export const SET_TOKEN_TYPE = "setToken";
export const INVALIDATE_TOKEN_TYPE = "invalidateToken";
export const START_LOGIN_TYPE = "startLogin";
export const Actions = {
setToken: (token: string) => createAction(SET_TOKEN_TYPE, token),
invalidateToken: () => createAction(INVALIDATE_TOKEN_TYPE),
startLogin: () => createAction(START_LOGIN_TYPE)
};
export const reducer = createReducer<State, typeof Actions>(
{
[SET_TOKEN_TYPE]: ({ loginError, ...state }, action) => ({
...state,
token: action.payload,
loading: false
}),
[INVALIDATE_TOKEN_TYPE]: ({ token, ...state }) => state,
[START_LOGIN_TYPE]: ({ loginError, ...state }) => ({
...state,
loading: true
})
},
{
loading: false
}
);
{
"name": "typescript-mapped-types-redux",
"version": "1.0.0",
"description": "Example of redux factory with mapped actions types",
"main": "index.js",
"scripts": {
"build": "tsc -p tsconfig.json"
},
"author": "Mateusz Lewandowski",
"license": "MIT",
"devDependencies": {
"typescript": "^3.2.2"
}
}
export interface Action<T extends string> {
type: T;
}
export interface ActionWithPayload<T extends string, P> extends Action<T> {
payload: P;
}
export function createAction<T extends string>(type: T): Action<T>;
export function createAction<T extends string, P>(
type: T,
payload: P
): ActionWithPayload<T, P>;
export function createAction<T extends string, P>(type: T, payload?: P) {
return payload === undefined ? { type } : { type, payload };
}
export type ActionCreator<T extends string> = (...args: any) => Action<T>;
export type ActionsCreators = {
[creator: string]: ActionCreator<any>;
};
export type ActionsUnion<Actions extends ActionsCreators> = ReturnType<
Actions[keyof Actions]
>;
export type ActionHandlers<ActionCreators extends ActionsCreators, State> = {
[K in ReturnType<ActionCreators[keyof ActionCreators]>["type"]]: (
state: State,
action: ReturnType<ActionCreators[K]>
) => State
};
export function createReducer<State, Actions extends ActionsCreators>(
handlers: ActionHandlers<Actions, State>,
initialState: State
) {
return (
state: State = initialState,
action: ActionsUnion<Actions>
): State => {
if (action.type in handlers) {
return handlers[action.type](state, action);
}
return state;
};
}
{
"compilerOptions": {
"noImplicitAny": true
},
"files": ["./example-usage.ts", "./redux-helpers.ts"]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment