Skip to content

Instantly share code, notes, and snippets.

@jantimon
Created September 21, 2020 12:30
Show Gist options
  • Save jantimon/f4072993432327c7a850d9ff592b397e to your computer and use it in GitHub Desktop.
Save jantimon/f4072993432327c7a850d9ff592b397e to your computer and use it in GitHub Desktop.
use reducer typings
export const actionCreator = <TState>() => <TOptions extends {}>(
actionHandler: ActionHandler<TOptions, TState>
) => actionHandler;
type ActionHandler<TOptions extends {}, TState extends {}> = (
state: TState,
options: TOptions
) => TState;
type ActionOptions<T> = T extends ActionHandler<infer TOptions, infer State>
? TOptions
: never;
type ActionState<T> = T extends ActionHandler<infer TOptions, infer TState>
? TState
: never;
type ActionMapper<T extends { [key: string]: ActionHandler<any, any> }> = {
readonly [P in keyof T]: {
type: P;
value: ActionOptions<T[P]>;
};
};
type ValueOf<T> = T[keyof T];
export type ActionTypes<
T extends { [key: string]: ActionHandler<any, any> }
> = ValueOf<ActionMapper<T>>;
export const createReducer = <T extends { [key: string]: ActionHandler<any, any> }> (actions: T) => {
type State = ActionState<ValueOf<T>>;
return (state: State, action: ActionTypes<T>) => actions[action.type](state, action.value);
}
export type ReducerDispatcher<T extends (state: any, action: any) => any> = T extends (state: any, action: infer TAction) => any ? React.Dispatch<TAction> : never
import { actionCreator, createReducer, ReducerDispatcher } from "./reducerTypings";
export interface NavigationStore {
/**
* Breadcrumb of all open Navigation level
*
* for example if the navigation is entirely close:
* `false`
*
* for example if only the root level is open:
*
* [{ href: '/', title: 'Home', items: [...]}]
*
* for example if only a child of the root level is open:
*
* [{ href: '/about', title: 'About us', items: [...]}, { href: '/', title: 'Home', items: [...]}]
*
*/
open: false | Array<NavigationLink>
/** The entire navigation tree structure */
navigationStructure: NavigationLink
}
export interface NavigationLink {
href: string,
title: string,
items: Array<NavigationLink>
}
const createAction = actionCreator<NavigationStore>();
/**
* Create a typed reducer for useReducer
*/
export const navigationReducer = createReducer({
openNavigation: createAction((state: NavigationStore, option: {}) => {
return {...state, open: [state.navigationStructure] }
}),
closeNavigation: createAction((state: NavigationStore, option: {}) => {
return {...state, open: false }
}),
});
export type NavigationDispatcher = ReducerDispatcher<typeof navigationReducer>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment