You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于传入的EventEmitter子类声明仅含可用事件名的类型?

解决TypeScript中EventEmitter子类事件名推断错误的问题

问题原因

你遇到的问题是因为TypeScript处理重载方法时,Parameters<T['on']>[0]只会提取on方法第一个重载的事件名参数类型,而process.stdin(属于stream.Readable类型)的on方法第一个重载对应的是'timeout'事件,导致编译器只允许传入这个事件名,无法识别其他合法事件。

解决方案

利用Node.js的EventEmitter泛型定义,提取子类的所有合法事件名联合类型,替代直接通过Parameters获取重载参数的方式:

import EventEmitter from "events";

// 提取EventEmitter子类的所有合法事件名
type EventNames<T extends EventEmitter> = T extends EventEmitter<infer EventMap> 
  ? keyof EventMap 
  : string | symbol;

class ConditionRemovableSubscriber<
  T extends EventEmitter,
  E extends EventNames<T>
> {
  constructor(
    readonly target: T,
    readonly eventName: E,
    // 自动推断监听函数的参数类型,替代any[]
    readonly listener: (...args: Parameters<Extract<T['on'], (e: E, l: infer L) => void>>[1]) => void
  ) {
    // 示例:完成事件订阅
    this.target.on(this.eventName, this.listener);
  }

  // 示例:实现自动取消订阅的方法
  unsubscribe() {
    this.target.off(this.eventName, this.listener);
  }
}

// 现在可以正常传入'data'等所有合法事件名,无编译错误
const check = new ConditionRemovableSubscriber(process.stdin, 'data', (chunk) => {
  console.log(chunk.toString());
});

关键说明

  1. EventNames类型:通过T extends EventEmitter<infer EventMap>提取子类的事件映射表EventMap,再取keyof EventMap得到所有合法事件名的联合类型,也就是你期望的'close' | 'connect' | 'data' | 'drain' | 'end' | 'error' | 'lookup' | 'ready' | 'timeout'。
  2. 类型安全的监听函数:替换原来的any[],通过Extract<T['on'], (e: E, l: infer L) => void>提取对应事件的监听函数类型,确保参数类型完全匹配。
  3. 自动取消订阅:示例中添加了unsubscribe方法,你可以根据自己的需求扩展条件取消的逻辑(比如满足特定条件时自动调用)。

内容的提问来源于stack exchange,提问作者Konservator

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 05:45:22