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

Typescript如何为支持多事件数组的Event API做类型标注?

解决方案

现有基础实现对齐

先对齐你提到的现有基础类型定义,方便后续扩展:

// 业务数据类型
type Apple = { id: string; weight: number };
type Peach = { id: string; sweetness: number };

// 事件类型
type AddAppleEvent = { type: 'addApple'; data: Apple };
type AddPeachEvent = { type: 'addPeach'; data: Peach };

// 事件名-事件类型映射
interface EventMap {
  addApple: AddAppleEvent;
  addPeach: AddPeachEvent;
}

核心扩展实现

你需要先定义工具类型处理联合转交叉逻辑,再给on方法加函数重载即可:

// 工具类型:联合类型转交叉类型
type UnionToIntersection<U> = 
  (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;

class EventApi {
  // 原有单事件监听签名
  on<T extends keyof EventMap>(eventName: T, callback: (evt: EventMap[T]) => void): void;

  // 新增数组事件监听签名
  on<K extends (keyof EventMap)[]>(eventNames: [...K], callback: (evt: Partial<UnionToIntersection<EventMap[K[number]]>>) => void): void;

  // 运行时实现,原有逻辑不需要修改
  on(eventName: string | string[], callback: (evt: any) => void) {
    // 原有事件绑定逻辑
  }
}

这里用[...K]可变元组语法是为了让Typescript能正确推导传入的数组字面量的精确类型,而不是放宽为(keyof EventMap)[]通用类型。

效果验证

const api = new EventApi();

// 原有单事件用法,类型推导不受影响
api.on('addApple', evt => {
  console.log(evt.data.weight); // evt 类型为 AddAppleEvent,属性可直接访问
});

// 新增数组事件用法
api.on(['addApple', 'addPeach'], evt => {
  // evt 类型为 Partial<AddAppleEvent & AddPeachEvent>,所有属性可选
  console.log(evt.type); // 类型为 'addApple' | 'addPeach' | undefined
  console.log(evt.data?.weight); // 类型为 number | undefined
  console.log(evt.data?.sweetness); // 类型为 number | undefined
});

可选场景:回调参数为事件联合类型

如果你的业务逻辑是触发时只会传入其中一个事件的完整结构(而非合并所有属性的可选结构),可以直接简化签名,不需要联合转交叉:

class EventApi {
  // 原有单事件签名
  on<T extends keyof EventMap>(eventName: T, callback: (evt: EventMap[T]) => void): void;

  // 简化版多事件签名,回调入参为事件联合类型
  on<K extends (keyof EventMap)[]>(eventNames: [...K], callback: (evt: EventMap[K[number]]) => void): void;

  // 运行时实现同上
}

// 使用示例
api.on(['addApple', 'addPeach'], evt => {
  // evt 类型为 AddAppleEvent | AddPeachEvent
  if (evt.type === 'addApple') {
    console.log(evt.data.weight); // 类型收窄后可正常访问专属属性
  } else {
    console.log(evt.data.sweetness);
  }
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 22:51:03