Skip to content

Instantly share code, notes, and snippets.

@weeksie
Last active February 10, 2022 18:22
Show Gist options
  • Save weeksie/2c21ca51e1801f1f779a3d71882cfa42 to your computer and use it in GitHub Desktop.
Save weeksie/2c21ca51e1801f1f779a3d71882cfa42 to your computer and use it in GitHub Desktop.
Some example redux helpers for typescript projects. For illustrative purposes only, but really—you don't need another effing npm package, just write this stuff yourself.
declare global {
/**
extend this where needed
*/
export interface Meta {
session?: string;
receipt?: UUID;
}
}
export interface Action<T extends string, P extends unknown = never> {
type: T;
payload: P;
meta?: Meta;
error?: Error | string;
// Database properties
id?: Id;
version?: number;
sequence?: number;
sourceId?: Id;
createdAt?: Date;
}
type PayloadArgs<P = never> = [P] extends [never] ? [] : [P];
type ActionCreatorFactory = <T extends string, P = never>(type: T) =>
ActionCreator<T, P> extends { type: T } ? ActionCreator<T, P> : never;
export interface ActionCreator<T extends string, P = never> {
(...args: PayloadArgs<P>): Action<T, P>;
is: (a: any) => a is Action<T, P>;
type: T;
namespace: string;
}
export const create: ActionCreatorFactory = <T extends string, P, M>(type: T) => {
const creator: ActionCreator<T, P, M> = (payload: P, meta: M) => ({
type,
payload,
meta
});
creator.is = (a: any): a is Action<T, P, M> => a.type === type;
creator.type = type;
creator.namespace = type.split('/')[0];
return creator;
}
export const create: ActionCreatorFactory = <T extends string, P = never>(type: T) => {
const creator = (...args: PayloadArgs<P>): Action<T, P> => {
if (args.length) {
return {
type,
payload: args[0],
};
}
return {
type,
payload: undefined as never,
};
};
creator.is = (a: any): a is Action<T, P> => a.type === type;
creator.type = type;
creator.namespace = type.split('/')[0];
return creator;
}
/**
Usage:
const create = Actions.c<EditorAction>();
*/
export const c = <
Base extends BaseAction,
>() => <A extends Base, P = GetPayload<A>>(
type: A['type']
) =>
create<A['type'], P>(type);
import * as Actions from './actions';
type Start = Actions.Action<'app/start', { id: string; }>;
type Started = Actions.Action<'app/started', { app: App }>;
type AppAction = Start | Started;
const create = Actions.c<AppAction>();
const actions = {
start: create<Start>('app/start'),
started: create<Started>('app/started'),
};
type AppState = {
app?: App;
loading: boolean;
}
const defaultState: AppState = {
loading: false;
}
function reducer(state: AppState = defaultState, action: AppAction) {
switch (action.type) {
case actions.start.type:
return {
...state,
loading: true,
};
case actions.started.type:
return {
...state,
loading: false,
app: action.payload.app,
};
default:
return state;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment