如何基于传入的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()); });
关键说明
- EventNames类型:通过
T extends EventEmitter<infer EventMap>提取子类的事件映射表EventMap,再取keyof EventMap得到所有合法事件名的联合类型,也就是你期望的'close' | 'connect' | 'data' | 'drain' | 'end' | 'error' | 'lookup' | 'ready' | 'timeout'。 - 类型安全的监听函数:替换原来的
any[],通过Extract<T['on'], (e: E, l: infer L) => void>提取对应事件的监听函数类型,确保参数类型完全匹配。 - 自动取消订阅:示例中添加了
unsubscribe方法,你可以根据自己的需求扩展条件取消的逻辑(比如满足特定条件时自动调用)。
内容的提问来源于stack exchange,提问作者Konservator
相关产品推荐
相关产品推荐

