type ValidSelector = string | number | symbol export type Message = Args extends never[] ? { selector: Selector, args: [], callback: (result: Result) => void } : { selector: Selector, args: Args, callback: (result: Result) => void } type MessagesProduct = { [K in keyof I]: (I[K] extends (...args: infer P) => infer Q ? Message : never); } export type Messages = MessagesProduct[keyof I] export type Methods = { [S in M['selector']]: ( M extends Message ? (...args: P) => R : never); } export function perform, S extends ValidSelector, M extends Message>( i: I, m: M): void { m.callback(i[m.selector](... m.args)); } //--------------------------------------------------------------------------- interface I { m1(a: string, b: number): boolean; m2(): void; m3(n: number): void; m4(x: [string, string]): { k: string, j: string }; m5(a: string, b: string[]): number; v: string; w: number; } type M = Messages // type M = // | Message<"m1", [a: string, b: number], boolean> // | Message<"m2", [], void> // | Message<"m3", [n: number], void> // | Message<"m4", [x: [string, string]], { k: string; j: string }> // | Message<"m5", [a: string, b: string[]], number> // type M = // | { selector: "m1", args: [a: string, b: number], callback: (result: boolean) => void } // | { selector: "m2", args: [], callback: (result: void) => void } // | { selector: "m3", args: [n: number], callback: (result: void) => void } // | { selector: "m4", args: [x: [string, string]], callback: (result: { k: string; j: string }) => void } // | { selector: "m5", args: [a: string, b: string[]], callback: (result: number) => void } // type M = // | { selector: "m1", args: [string, number], callback: (result: boolean) => void } // | { selector: "m2", args: [], callback: (result: void) => void } // | { selector: "m3", args: [number], callback: (result: void) => void } // | { selector: "m4", args: [[string, string]], callback: (result: { k: string; j: string }) => void } // | { selector: "m5", args: [string, string[]], callback: (result: number) => void } type I2 = Methods // type I2 = { // m1: (a: string, b: number) => boolean; // m2: () => void; // m3: (n: number) => void; // m4: (x: [string, string]) => { k: string; j: string }; // m5: (a: string, b: string[]) => number; // }; type X = I2 const a = { a(): string { console.log('in a'); return 'hi'; }, b(): void { console.log('in b'); }, c(x: number): number { console.log('in c:', x); return x * 2; }, v(x: number, y: string, z: boolean): string { return `x ${x} y ${y} z ${z}`; }, }; type A = typeof a; type A1 = Messages; type A2 = Methods; const b: A2 = a; const aa = perform(a, { selector: 'a', args: [], callback: (_r: string) => {} }); const bb = perform(a, { selector: 'b', args: [], callback: (_r: void) => { console.log('bb'); } }); const cc = perform(a, { selector: 'c', args: [123], callback: (r: number) => { console.log('cc', r); } }); const vv = perform(a, { selector: 'v', args: [123, 'hi', true], callback: (r: string) => { console.log('vv', r); } }); perform({ a(): string { return 'hi' } }, { selector: 'a', args: [123], callback: (_r: string) => { console.log('x'); } });