Skip to content

Instantly share code, notes, and snippets.

@3mcd
Created September 27, 2022 19:53
Show Gist options
  • Save 3mcd/7ff134b11043245c2c8386bb9fbe1a51 to your computer and use it in GitHub Desktop.
Save 3mcd/7ff134b11043245c2c8386bb9fbe1a51 to your computer and use it in GitHub Desktop.
export default '';
type Tail<T extends any[]> = T extends [any, ...infer U] ? U : never;
type Dispatch = (state: any, ...args: any[]) => any;
type DispatchState<D extends Dispatch> = Parameters<D>[0] | ReturnType<D>;
type DispatchApi = { [key: string]: Dispatch };
type State<D extends DispatchApi> = {
[K in keyof D]: DispatchState<D[K]>;
}[keyof D];
type Effect<D extends DispatchApi, F extends Dispatch> = (
state: ReturnType<F>,
...args: Tail<Parameters<F>>
) => State<D> | Promise<State<D>> | Generator<State<D>> | AsyncGenerator<State<D>>;
type EffectApi<D extends DispatchApi> = { [K in keyof D]?: Effect<D, D[K]> };
type Unwrap<T> = T extends Promise<infer _>
? _
: T extends Generator<infer ItYield, infer ItReturn>
? void extends ItReturn
? ItYield
: ItReturn
: T extends AsyncGenerator<infer AsyncItYield, infer AsyncItReturn>
? void extends AsyncItReturn
? AsyncItYield
: AsyncItReturn
: T;
type UnwrapEffectState<D extends DispatchApi, T> = T extends State<D>
? T
: T extends void
? State<D>
: never;
type StoreMethods<D extends DispatchApi, E extends EffectApi<D>, S extends State<D>> = {
[K in Exclude<keyof D, keyof E>]: D[K] extends (state: S, ...args: infer A) => infer S1
? (...args: A) => Store<D, E, S1>
: never;
} & {
[K in keyof E & keyof D]: E[K] extends (...args: any[]) => infer R
? (
...args: Tail<Parameters<D[K]>>
) => R extends Promise<any> | AsyncGenerator<any>
? Promise<Store<D, E, UnwrapEffectState<D, Unwrap<R>>>>
: Store<D, E, UnwrapEffectState<D, Unwrap<R>>>
: never;
};
type Store<D extends DispatchApi, E extends EffectApi<D>, S extends State<D>> = {
state: S;
} & StoreMethods<D, E, S>;
function createStore<D extends DispatchApi, E extends EffectApi<D>, S extends State<D>>(
dispatch: D,
effects: E,
state: S,
): Store<D, E, S> {
return undefined as any as Store<D, E, S>;
}
type Initial = { fetching: false };
type Fetching = { fetching: true };
type FetchSuccess = Initial & { success: true; data: unknown };
type FetchFailure = Initial & { success: false; error: Error };
const store = createStore(
{
fetch(_state: Initial, _page: number = 0): Fetching {
return { fetching: true };
},
},
{
async *fetch(_state, page) {
try {
let response = await fetch(`localhost:3000?page=${page}`);
if (response.ok) {
let data = await response.json();
return { fetching: false, success: true, data } as FetchSuccess;
} else {
throw new Error();
}
} catch (error) {
return { fetching: false, success: false, error } as FetchFailure;
}
},
},
{ fetching: false } as Initial,
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment