ARTICLE AD BOX
I basically tried this and it works; I get an error if I mismatch an event type in the ButtonEventMap record and get type inference for the listener's Event parameter.
class UIComponent { on(eventType: string, fn: (e: Event) => void): void {} } class Button extends UIComponent { on<K extends keyof ButtonEventMap>(eventType: K, fn: (e: ButtonEventMap[K]) => void): void; on(eventType: string, fn: (e: Event) => void): void { super.on(eventType, fn) } } type ButtonEventMap = { click: MouseEvent, }; const button = new Button(); button.on("click", e => {});However, it's a bit verbose to include this on() override into every subclass of UIComponent; so I tried implementing an interface, and it doesn't do anything:
interface IEventTarget<EventMap> { on<K extends keyof EventMap>(eventType: K, fn: (e: EventMap[K]) => void): void; on(eventType: string, fn: (e: Event) => void): void } class UIComponent { on(eventType: string, fn: (e: Event) => void): void {} } class Button extends UIComponent implements IEventTarget<ButtonEventMap> { // } type ButtonEventMap = { click: MouseEvent, }; const button = new Button(); button.on("click", e => {});With that, button.on() is entirely dynamic. I want the same behavior as the initial code in the question, but using an implemented interface.
