Skip to content

Instantly share code, notes, and snippets.

@gund
Last active January 26, 2021 23:15
Show Gist options
  • Save gund/79dbb8fc82bfc41a66df46053155c423 to your computer and use it in GitHub Desktop.
Save gund/79dbb8fc82bfc41a66df46053155c423 to your computer and use it in GitHub Desktop.
Extendable actions service library
// Type Utils
interface DefaultChecker {
__defaultChecker: true;
}
type Default<T, D> = T | DefaultChecker extends DefaultChecker ? D : T;
type MapTo<T, E, M> = T extends E ? M : T;
type AsKeyOf<K, T> = K extends keyof T ? K : never;
type Observable<T> = {};
interface Type<T> extends AbstractType<T> {
new (...args: any[]): T;
}
interface AbstractType<T> {
prototype: T;
}
interface Generics<T extends any[] = any[]> {
__generics: T;
}
type InferGenerics<T> = T extends Generics<infer G> ? G : never;
type InferGeneric<T, I> = MapTo<
InferGenerics<T>[AsKeyOf<I, InferGenerics<T>>],
undefined,
never
>;
type ExtendGenerics<G extends Generics, T extends any[]> = Omit<
G,
'__generics'
> &
Generics<[...G['__generics'], ...T]>;
// Implementation
type RegistryType<R> = Default<keyof R, string>;
type GetFromRegistry<T, R, D = never> = Default<R[AsKeyOf<T, R>], D>;
type GetRegistryKey<V, I extends keyof V> = V[I];
type RegistryDeclaration<R> = { [P in keyof R]: Type<R[P]> };
interface ActionsRegistry {
// type: ActionHandler;
}
type ActionType = RegistryType<ActionsRegistry>;
type InferAction<T extends ActionType> = GetFromRegistry<
T,
ActionsRegistry,
ActionHandler
>;
type InferActionContext<T extends ActionType> = InferGeneric<InferAction<T>, 0>;
type InferActionReturn<T extends ActionType> = InferGeneric<InferAction<T>, 1>;
interface ActionConfig {
type: ActionType;
// Different types MAY have extra config
[k: string]: unknown;
}
interface ActionHandler<C = unknown, R = unknown> extends Generics<[C, R]> {
handleAction(injector: unknown, config: ActionConfig, context: C): Observable<R>;
}
interface ActionsService {
trigger<C extends ActionConfig>(
injector: unknown,
config: C,
context: InferActionContext<C['type']>,
): Observable<InferActionReturn<C['type']>>;
}
interface ActionsModule {
withActions(actions: RegistryDeclaration<ActionsRegistry>);
}
// Project level example
interface TableConfig {
id: string;
}
interface TableActionHandler<TC, R = unknown>
extends ExtendGenerics<ActionHandler<TableConfig, R>, [TC]> {}
type t1 = InferActionContext<'test3'>;
type t2 = InferActionReturn<'test3'>;
type t3 = InferGeneric<InferAction<'test3'>, 2>;
interface ActionsRegistry {
test: ActionHandler<boolean, string>;
}
interface ActionsRegistry {
test2: ActionHandler<string, number>;
}
interface ActionsRegistry {
test3: TableActionHandler<'c', 'a'>;
}
let actionsService: ActionsService;
let actionConfig: ActionConfig;
actionsService.trigger(null, actionConfig, true);
actionsService.trigger(null, { type: 'test' }, true);
actionsService.trigger(null, { type: 'test2' }, 'aaa');
actionsService.trigger(null, { type: 'test3' }, { id: '123' });
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment