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

如何实现TypeScript类型EventMapOf<T>提取EventTarget的事件映射?

提取EventTarget子类的自定义事件映射类型:EventMapOf实现方案

实现代码

// 辅助类型:根据事件名提取对应的事件类型
type EventType<T extends EventTarget, K extends string> = 
  Parameters<Extract<T['addEventListener'], (type: K, ...args: any[]) => void>>[1] extends 
    (evt: infer E) => void 
      ? E 
      : never;

// 核心类型:从EventTarget子类中提取事件映射
type EventMapOf<T extends EventTarget> = {
  [K in Exclude<Parameters<T['addEventListener']>[0], string>]: EventType<T, K>;
};

原理说明

  1. 提取事件名联合类型:Parameters<T['addEventListener']>[0] 获取addEventListener方法第一个参数的所有可能类型,对于自定义子类来说,这个类型是「自定义事件名字面量 + string」的联合。
  2. 过滤基础重载干扰:用Exclude<..., string>剔除基础EventTarget重载中的string类型,只保留子类自定义的事件名字面量。
  3. 匹配对应事件类型:EventType<T, K>通过Extract筛选出匹配当前事件名K的重载,再从监听函数的参数中推断出对应的事件类型E。
  4. 构建事件映射对象:通过索引类型遍历过滤后的事件名,将每个事件名映射到对应的事件类型,最终得到完整的自定义事件映射。

示例验证

// 定义自定义事件映射
interface FooEventMap {
  'custom-click': CustomEvent<{ id: number }>;
  'data-update': Event;
}

// 实现EventTarget子类
interface Foo extends EventTarget {
  addEventListener<K extends keyof FooEventMap>(
    type: K, 
    listener: (evt: FooEventMap[K]) => void, 
    options?: boolean | AddEventListenerOptions
  ): void;
  // 保留基础EventTarget的重载
  addEventListener(
    type: string, 
    listener: EventListenerOrEventListenerObject, 
    options?: boolean | AddEventListenerOptions
  ): void;
}

// 提取事件映射
type FooExtractedEventMap = EventMapOf<Foo>;
// 结果:{ 'custom-click': CustomEvent<{ id: number }>; 'data-update': Event; }

解决之前的问题

  • 基础EventTarget的重载会将事件名合并为string类型,通过Exclude可以精准过滤掉这个通用类型,分离出自定义字面量事件名。
  • 泛型约束推断失败是因为重载函数的类型无法直接被自动解构,通过Extract筛选特定重载、结合infer推断事件类型,可以绕过这个限制。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 16:13:42