68 lines
2.9 KiB
TypeScript
68 lines
2.9 KiB
TypeScript
// This Tuple type (and tuple() function) is a hack to induce
|
|
// TypeScript to infer tuple types rather than array types. (Source:
|
|
// https://github.com/microsoft/TypeScript/issues/27179#issuecomment-422606990)
|
|
//
|
|
// Without it, [123, 'hi', true] will often get the type (string |
|
|
// number | boolean)[] instead of [number, string, boolean].
|
|
//
|
|
export type Tuple = any[] | [any];
|
|
export const tuple = <A extends Tuple>(... args: A) => args;
|
|
|
|
// Type ValidSelector captures TypeScript's notion of a valid object
|
|
// property name.
|
|
//
|
|
export type ValidSelector = string | number | symbol;
|
|
|
|
export type EventMessage<Selector extends ValidSelector, Args extends any[]> =
|
|
{ selector: Selector, args: Args };
|
|
|
|
export type RequestMessage<Selector extends ValidSelector, Args extends any[], Result extends Exclude<any, void>> =
|
|
{ selector: Selector, args: Args, callback: (result: Result) => void };
|
|
|
|
export type Message<Selector extends ValidSelector, Args extends any[], Result> =
|
|
void extends Result ? EventMessage<Selector, Args> : RequestMessage<Selector, Args, Result>;
|
|
|
|
// Function message() is needed for similar reasons to tuple() above:
|
|
// to help TypeScript infer the correct literal type for the selector
|
|
// (as well as the arguments).
|
|
//
|
|
export const message = <S extends ValidSelector, A extends Tuple, R>(m: Message<S, A, R>) => m;
|
|
|
|
type MessagesProduct<I, ContextArgs extends any[]> = {
|
|
[K in keyof I]: (I[K] extends (...args: [...ContextArgs, ...infer P]) => infer Q
|
|
? Message<K, P, Q>
|
|
: never);
|
|
};
|
|
|
|
export type Messages<I, ContextArgs extends any[] = []> = MessagesProduct<I, ContextArgs>[keyof I];
|
|
|
|
export type Methods<M extends { selector: ValidSelector }, ContextArgs extends any[] = []> = {
|
|
[S in M['selector']]: (
|
|
M extends RequestMessage<S, infer P, infer R>
|
|
? (void extends R ? never : (...args: [...ContextArgs, ...P]) => R)
|
|
: (M extends EventMessage<S, infer P>
|
|
? (...args: [...ContextArgs, ...P]) => void
|
|
: never));
|
|
};
|
|
|
|
export function perform<I extends Methods<M, ContextArgs>,
|
|
S extends ValidSelector,
|
|
M extends RequestMessage<S, Tuple, any>,
|
|
ContextArgs extends any[] = []>
|
|
(i: I, m: M, ...ctxt: ContextArgs): (M extends RequestMessage<S, Tuple, infer R> ? R : never);
|
|
export function perform<I extends Methods<M, ContextArgs>,
|
|
S extends ValidSelector,
|
|
M extends EventMessage<S, Tuple>,
|
|
ContextArgs extends any[] = []>
|
|
(i: I, m: M, ...ctxt: ContextArgs): void;
|
|
export function perform<I extends Methods<M, ContextArgs>,
|
|
S extends ValidSelector,
|
|
M extends RequestMessage<S, Tuple, any>,
|
|
ContextArgs extends any[] = []>
|
|
(i: I, m: M, ...ctxt: ContextArgs): any
|
|
{
|
|
const r = i[m.selector](...ctxt, ... m.args);
|
|
m.callback?.(r);
|
|
return r;
|
|
}
|