← 中文资源

简体中文版本

带监听器上限的类型化事件发射器

事件名与负载类型由泛型事件表在编译期约束,每个事件限制监听器数量,on 返回可用的退订函数。

阅读英文原文

功能说明

Node 的 EventEmitter 接受任意事件名和负载,事件名拼错时监听永远不触发,负载字段也无人校验。TypedEmitter 用泛型事件表约束 on、off、emit 的签名,内部按事件维护监听器集合,并在注册时检查数量上限,避免监听器无界增长。它适合应用内部事件总线、任务状态通知等场景。

关键代码

类接受 TEvents 泛型,内部用 Map 以 keyof TEvents 为键保存 Set,负载统一存为 never 以便安全擦除类型。构造时可传 maxListeners,默认 10,非正整数抛 RangeError。on 在集合不存在时创建,超过上限抛错,随后把监听器加入集合并返回调用 off 的闭包。off 删除监听器并在集合为空时移除该事件。emit 先复制集合再逐个调用,避免派发过程中删除导致跳过。listenerCount 返回当前数量。

export type EventMap = Record<string, unknown>;

export class TypedEmitter<TEvents extends EventMap> {
  private readonly listeners = new Map<keyof TEvents, Set<(payload: never) => void>>();

使用注意

派发是同步的,某个监听器抛错会中断后续监听器并把异常抛给 emit 调用方。示例不提供 once 包装、异步迭代或通配符事件。上限按事件单独计算,不是全局总数。监听器仍可能被长期持有,组件卸载时应调用 on 返回的退订函数或主动 off。事件表需满足 Record<string, unknown> 约束,通常用交叉类型补充索引签名。

验证方式

运行随附测试:注册两个事件并 emit,确认负载字段按类型传入回调;调用 off 后计数归零且不再触发;使用 on 返回的函数退订后回调不执行;maxListeners 设为 2 时第三次注册应抛错;向没有监听器的事件 emit 不应抛异常。