Skip to content

Instantly share code, notes, and snippets.

@weeksie
Last active February 10, 2022 18:22
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