I created a custom EventEmitter subclass with function overloading to automatically type the callback when using addListener with a specific string:
const codes = {
code1: 'code1',
code2: 'code2',
} as const;
type CodeType = typeof codes;
interface Code1Interface { a: string };
interface Code2Interface { b: boolean };
class MyEmitter { // extends ...
public addListener(code: CodeType['code1'], callback: (event: Code1Interface) => void): void;
public addListener(code: CodeType['code2'], callback: (event: Code2Interface) => void): void;
public addListener(code: CodeType[keyof CodeType], callback: (event: any) => void): void; // fallback
public addListener(code: any, callback: any) {
// call the parent addListener here
}
}
Despite this setup, I encountered an issue where TypeScript couldn't infer the return type for a function that wraps addListener into a promise:
function getEvent(code: CodeType[keyof CodeType]) { // returns Promise<unknown>
return new Promise((resolve, reject) => {
const emitter = new MyEmitter();
emitter.addListener(code, (event) => resolve(event));
})
}
getEvent(codes.code1).then(x => x) // x has type unknown
I'm wondering if it's possible to pass on the interface type from the function overloading to the caller of getEvent or implement a more generic mapping approach that can be reused in the MyEmitter class.