Patrones
¿Cómo mantengo cada nombre de evento emparejado con su carga y listener correctos?
Usa un mapa de eventos y un parámetro de clave para que el nombre seleccione la carga en ambos métodos.
La receta
type Handler<Events, K extends keyof Events> = (payload: Events[K]) => void type TypedEmitter<Events extends object> = { on<K extends keyof Events>(event: K, handler: Handler<Events, K>): void emit<K extends keyof Events>(event: K, payload: Events[K]): void } type Events = { saved: { id: string } failed: { message: string } } declare const bus: TypedEmitter<Events> bus.on('saved', payload => payload.id) bus.emit('failed', { message: 'offline' })
La compilación compila esto y comprueba cada resultado de abajo.
Cómo funciona
- 01
K extends keyof Events
La clave solo puede ser un nombre de evento declarado.
- 02
payload: Events[K]
El acceso indexado convierte el nombre elegido en su carga exacta.
Lo que obtienes
keyof Events extends 'saved' | 'failed' ? ('saved' | 'failed' extends keyof Events ? true : false) : false
→trueParameters<Handler<Events, 'saved'>>[0]→{ id: string; }Parameters<Handler<Events, 'failed'>>[0]→{ message: string; }
Dónde falla
El tipo solo protege llamadas que pasan por esta interfaz. Un emisor débilmente tipado por debajo aún puede entregar datos inválidos, así que valida eventos que crucen procesos o redes.
Conclusión
Un mapa más `K extends keyof Map` es la forma estándar de correlacionar un nombre con sus datos.
