Skip to content

Instantly share code, notes, and snippets.

@JLarky
Last active December 17, 2019 16:31
Show Gist options
  • Save JLarky/93d6b5c87a7dc5bd181f8a6cc9210d5f to your computer and use it in GitHub Desktop.
Save JLarky/93d6b5c87a7dc5bd181f8a6cc9210d5f to your computer and use it in GitHub Desktop.
type safe redux actions (typescript)
// like typesafe-actions
export type AnyAction<T extends string, A> = A & {
type: T;
};
export function createAction<T extends string, P, AC extends (...args: any[]) => AnyAction<T, P>>(
type: T,
map: AC
): AC & { type: T } {
const actionCreator = map as AC & { type: T };
actionCreator.type = type;
return actionCreator;
}
export function createEmptyAction<T extends string>(type: T) {
return createAction(type, () => ({ type }));
}
export function createEmptyMapAction<T extends string, P>(type: T, payloadCreator: () => P) {
return createAction(type, () => ({ type, payload: payloadCreator() }));
}
export function createMapAction<T extends string, A, P>(type: T, payloadCreator: (a: A) => P) {
return createAction(type, (a: A) => ({ type, payload: payloadCreator(a) }));
}
// like react-redux-typescript
export declare type OfActionCreator<
T extends {
type: string;
}
> = ((...args: any[]) => T) & { type: T["type"] };
export function isOfAction<A extends { type: string }, T1 extends A>(
ofActionCreator: OfActionCreator<T1>
): (action: A) => action is T1 {
const { type } = ofActionCreator;
return (action: A): action is T1 => action.type === type;
}
export type OfActionCreators<
T1 extends { type: string },
T2 extends { type: string } = any,
T3 extends { type: string } = any,
T4 extends { type: string } = any,
T5 extends { type: string } = any
> = any[] &
(
| [OfActionCreator<T1>]
| [OfActionCreator<T1>, OfActionCreator<T2>]
| [OfActionCreator<T1>, OfActionCreator<T2>, OfActionCreator<T3>]
| [OfActionCreator<T1>, OfActionCreator<T2>, OfActionCreator<T3>, OfActionCreator<T4>]
| [
OfActionCreator<T1>,
OfActionCreator<T2>,
OfActionCreator<T3>,
OfActionCreator<T4>,
OfActionCreator<T5>
]);
export function isOfActions<
A extends { type: string },
T1 extends A,
T2 extends A,
T3 extends A,
T4 extends A,
T5 extends A
>(
ofActionCreators:
| OfActionCreators<T1>
| OfActionCreators<T1, T2>
| OfActionCreators<T1, T2, T3>
| OfActionCreators<T1, T2, T3, T4>
| OfActionCreators<T1, T2, T3, T4, T5>
): (action: A) => action is [T1, T2, T3, T4, T5][number] {
return (action: A): action is T1 => ofActionCreators.some(({ type }) => action.type === type);
}
export function getReturnOfExpression<RT>(_expression: (...params: any[]) => RT): RT {
return (undefined as any) as RT;
}
// ALIAS
export const returntypeof = getReturnOfExpression;
@JLarky
Copy link
Author

JLarky commented Feb 14, 2018

Example usage

const actionCreators = {
    test: createEmptyAction("TEST"),
    inc: createAction("INC", (amount: number) => ({ type: "INC", payload: amount })),
    dec: createMapAction("DEC", (amount: number) => amount),
};

const events = [actionCreators.test(), actionCreators.inc(2), actionCreators.dec(1)];
events.forEach(action => {
    if (action.type === actionCreators.test.type) {
        console.log("test action happened");
    }
});
let state = 0;
events.filter(isOfActions([actionCreators.inc, actionCreators.dec])).forEach(action => {
    state = state + (action.type === "INC" ? 1 : -1) * action.payload;
    console.log("new state", state);
});

Edit typesafe action creators

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment