type PickMembers = { [P in K]: T[P] }; type BadCallback = (arg?: any) => void; class FakeEventEmitter { data: { [key: string]: BadCallback[]; } = {}; on(name: string, callback: BadCallback) { if (!this.data[name]) { this.data[name] = []; } this.data[name].push(callback); } emit(name: string, data?: any) { if (!this.data[name]) { return; } this.data[name].forEach(cb => { cb(data); }); } } type KeysOfType< Terface, Filter, K extends keyof Terface = keyof Terface > = K extends (Terface[K] extends Filter ? K : never) ? K : never; interface TypedEventEmitter< Events, VoidKeys extends KeysOfType = KeysOfType, NotVoidKeys extends Exclude = Exclude > { on( eventName: K, callback: (payload: Events[K]) => void ) : void; emit(eventName: K, payload: Events[K]) : void; on( eventName: K, callback: () => void ) : void; emit(eventName: K) : void; } type StrictEventEmitter = TypedEventEmitter & PickMembers< EventEmitter, Exclude> >; interface Point { x: number; y: number; } interface Events { BasicMessage: string; NumberMessage: number; PosMsg: Point; VoidMsg: void; } const ee: StrictEventEmitter = new FakeEventEmitter(); ee.emit("VoidMsg"); ee.emit("PosMsg", 145); ee.emit("PosMsg", { x: 1, y: 2 });