type EventMap = Record<string, any>;
type EventKey<T extends EventMap> = string & keyof T;
type EventCallback<T> = (payload: T) => void;
class TypedEventEmitter<T extends EventMap> {
private listeners: {
[K in keyof T]?: EventCallback<T[K]>[];
} = {};
on<K extends EventKey<T>>(event: K, callback: EventCallback<T[K]>): void {
if (!this.listeners[event]) {
this.listeners[event] = [];
}
this.listeners[event]!.push(callback);
}
off<K extends EventKey<T>>(event: K, callback: EventCallback<T[K]>): void {
const callbacks = this.listeners[event];
if (callbacks) {
this.listeners[event] = callbacks.filter((cb) => cb !== callback);
}
}
emit<K extends EventKey<T>>(event: K, payload: T[K]): void {
this.listeners[event]?.forEach((callback) => callback(payload));
}
}Usage
interface AppEvents {
userLogin: { userId: string; timestamp: Date };
userLogout: { userId: string };
error: { message: string; code: number };
}
const emitter = new TypedEventEmitter<AppEvents>();
// Type-safe event handling
emitter.on('userLogin', ({ userId, timestamp }) => {
console.log(`User ${userId} logged in at ${timestamp}`);
});
// Type-safe emit
emitter.emit('userLogin', {
userId: '123',
timestamp: new Date(),
});



