// 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 = (... 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: Selector, args: Args }; export type RequestMessage> = { selector: Selector, args: Args, callback: (result: Result) => void }; export type Message = void extends Result ? EventMessage : RequestMessage; // 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 = (m: Message) => m; type MessagesProduct = { [K in keyof I]: (I[K] extends (...args: [...ContextArgs, ...infer P]) => infer Q ? Message : never); }; export type Messages = MessagesProduct[keyof I]; export type Methods = { [S in M['selector']]: ( M extends RequestMessage ? (void extends R ? never : (...args: [...ContextArgs, ...P]) => R) : (M extends EventMessage ? (...args: [...ContextArgs, ...P]) => void : never)); }; export function perform, S extends ValidSelector, M extends RequestMessage, ContextArgs extends any[] = []> (i: I, m: M, ...ctxt: ContextArgs): (M extends RequestMessage ? R : never); export function perform, S extends ValidSelector, M extends EventMessage, ContextArgs extends any[] = []> (i: I, m: M, ...ctxt: ContextArgs): void; export function perform, S extends ValidSelector, M extends RequestMessage, ContextArgs extends any[] = []> (i: I, m: M, ...ctxt: ContextArgs): any { const r = i[m.selector](...ctxt, ... m.args); m.callback?.(r); return r; }