Skip to content

Instantly share code, notes, and snippets.

@rradczewski
Created October 21, 2019 20:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rradczewski/db4f1fa0ecc0b64756e5d35e722e5831 to your computer and use it in GitHub Desktop.
Save rradczewski/db4f1fa0ecc0b64756e5d35e722e5831 to your computer and use it in GitHub Desktop.
An almost-fully-typed redux-like projection lib
export type Event<T extends string, P> = {
type: T;
meta: {
time: string;
};
payload: P;
};
type EventCreator<T extends string, P> = (
payload: P,
meta?: Object
) => Event<T, P>;
const EventCreator: <T extends string, P = undefined>(
type: T
) => EventCreator<T, P> = type => (payload, meta = {}) => ({
type,
payload,
meta: { ...meta, time: new Date().toISOString() }
});
const on = <X, S>(
type: X extends EventCreator<infer T, infer _> ? T : never,
fun: (
state: S,
event: X extends EventCreator<infer T, infer P> ? Event<T, P> : never
) => S
) => ({ type, fun });
type Projection<S> = (e: Event<any, any>[]) => S;
type EventHandler<S, E> = {
type: E extends Event<infer T, infer _> ? T : never;
fun: (state: S, event: E extends Event<infer T, infer P> ? E : never) => S;
};
const project = <S>(...handlers: EventHandler<S, Event<string, any>>[]) => (
initialState: S
): Projection<S> => (events: Event<string, any>[]): S =>
events.reduce(
(state, event) =>
handlers.find(h => h.type === event.type)!.fun(state, event),
initialState
);
const HelloWorld: EventCreator<"HELLO_WORLD", undefined> =
EventCreator("HELLO_WORLD");
const ByeWorld: EventCreator<"BYE_WORLD", { name: string }> =
EventCreator<"BYE_WORLD", { name: string }>("BYE_WORLD");
const exampleProjection: Projection<{ name: string }> = project<{
name: string;
}>(
on<typeof HelloWorld, { name: string }>(
"HELLO_WORLD",
(state, _) => state
),
on<typeof ByeWorld, { name: string }>(
"BYE_WORLD",
(_, e) => ({
name: e.payload.name
})
)
)({ name: "" });
const result: { name: string } = exampleProjection([
HelloWorld(undefined),
ByeWorld({ name: "Pink Floyd" })
]);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment