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

TypeScript中为泛型回调类型传递类型化参数的问题

TypeScript Observer 类型化回调解决方案

问题背景

我需要实现一个支持传递payload的Observer:

  • Observer本身不关心回调接收的参数类型
  • 注册回调时,希望能为参数添加类型声明,避免使用any就能访问参数属性
  • 当前实现用unknown类型时,无法直接传递类型化参数;尝试泛型时,又遇到ObservableObj接口在构造阶段就被初始化,此时还不知道后续回调的参数类型的问题

解决方案

通过将on方法设计为泛型方法,同时让ObservableObj支持泛型参数,就能在注册回调时动态指定参数类型,同时兼容不同事件的不同参数类型:

改造后的完整代码

interface ObservableObj<T = unknown> {
  id: string;
  once: boolean;
  callback: (data?: T) => void;
}

export default class Observable {
  // 用unknown作为基础类型,兼容所有事件的参数类型
  private listeners: Map<string, Array<ObservableObj<unknown>>>;

  constructor() {
    this.listeners = new Map<string, Array<ObservableObj<unknown>>>();
  }

  // 将on方法设为泛型方法,允许调用时指定参数类型T
  public on<T = unknown>(
    eventType: string,
    callback: (data?: T) => void,
    once = false
  ): { id: string; eventType: string } {
    if (!this.listeners.get(eventType)) {
      this.listeners.set(eventType, []);
    }

    const id = this.generateId();
    // 类型断言,因为当前T属于unknown的子集
    this.listeners.get(eventType)?.push({ id, callback, once } as ObservableObj<unknown>);
    return { id, eventType };
  }

  // fire方法添加泛型,确保触发时的数据类型与回调匹配
  public fire<T = unknown>(eventType: string, data?: T) {
    const eventListeners = this.listeners.get(eventType);
    if (!eventListeners || eventListeners.length === 0) {
      return;
    }

    let i = eventListeners.length - 1;
    while (i >= 0) {
      const listener = eventListeners[i];
      const f = listener.callback as (data?: T) => void;
      
      if (listener.once) {
        eventListeners.splice(i, 1);
      }
      i--;
      f(data);
    }
  }

  private generateId(): string {
    return Math.random().toString(36).substring(2, 9);
  }
}

// 使用示例:注册回调时指定参数类型
const observable = new Observable();
observable.on<{ prop: boolean }>('test-event', (data) => {
  // 这里data自动推断为{ prop: boolean },无需any
  console.log(data.prop);
});
observable.fire<{ prop: boolean }>('test-event', { prop: true });

// 不同事件可以用不同类型
observable.on<number>('count-event', (count) => {
  console.log(count + 1);
});
observable.fire<number>('count-event', 5);

关键改动说明

  1. ObservableObj泛型化:给ObservableObj添加泛型参数T,默认值为unknown,让回调的参数类型可以动态绑定
  2. on方法泛型化:将on方法设计为泛型方法,调用时可以显式指定参数类型T,TypeScript会自动推断回调的参数类型,无需手动标注
  3. fire方法泛型化:泛型设计确保触发事件时传递的数据类型与该事件注册的回调期望类型一致,增强类型检查
  4. 类型兼容处理:通过类型断言将特定类型的ObservableObj转为ObservableObj<unknown>,因为所有类型都是unknown的子集,保证Map中存储的类型统一

效果验证

注册回调时,无需使用any就能获得完整的类型提示;触发事件时,如果传递的数据类型不匹配,TypeScript会给出编译错误,避免运行时问题。

内容的提问来源于stack exchange,提问作者Дмитрий Соловьев

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 22:41:18