Created
February 17, 2021 11:25
-
-
Save tonyg/a1f57d5d52f4363ddf03d7a06c69788f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment