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

如何在TypeScript中实现无any类型的PubSub模式?

改进TypeScript PubSub实现以避免any类型

你当前的PubSub实现存在两处any类型的不合理使用:回调函数的默认泛型参数、publish方法的value参数。我们可以通过泛型类实现事件类型与回调参数的强绑定,彻底消除any,同时优化订阅逻辑以支持多订阅者(原代码会覆盖同一事件的之前回调,不符合常规PubSub设计)。

原代码问题分析

  • CallbackFunc<T = any> 使用any作为默认类型,导致回调参数类型完全不明确
  • publish方法的value: any无法保证与对应事件的回调参数类型匹配
  • 单事件仅支持一个订阅者,实用性不足

修改后的代码

// 定义回调函数类型,移除any默认值
type CallbackFunc<T> = (arg: T) => void;

// 泛型类,Events参数定义事件名到对应参数类型的映射
export class PubSub<Events extends Record<string, unknown>> {
  // 每个事件对应一组回调函数数组
  private events: Record<keyof Events, CallbackFunc<Events[keyof Events]>[]> = {} as any;
  private static instance: PubSub<any>;

  // 单例方法适配泛型
  static getInstance<Events extends Record<string, unknown>>(): PubSub<Events> {
    if (!PubSub.instance) {
      PubSub.instance = new PubSub<Events>();
    }
    return PubSub.instance as PubSub<Events>;
  }

  // publish方法根据事件名自动推断参数类型
  publish<EventName extends keyof Events>(eventName: EventName, value: Events[EventName]): void {
    const handlers = this.events[eventName];
    if (handlers) {
      handlers.forEach(handler => handler(value));
    }
  }

  // subscribe方法确保回调参数类型与事件严格匹配
  subscribe<EventName extends keyof Events>(eventName: EventName, handler: CallbackFunc<Events[EventName]>): void {
    if (!this.events[eventName]) {
      this.events[eventName] = [];
    }
    this.events[eventName].push(handler);
  }

  // 可选:添加取消订阅功能
  unsubscribe<EventName extends keyof Events>(eventName: EventName, handler: CallbackFunc<Events[EventName]>): void {
    const handlers = this.events[eventName];
    if (handlers) {
      this.events[eventName] = handlers.filter(h => h !== handler);
    }
  }
}

使用示例

// 提前定义所有事件及其参数类型的映射
type AppEvents = {
  userLogin: { userId: string; username: string };
  messageReceived: { content: string; timestamp: number };
};

// 获取绑定了事件类型的PubSub实例
const pubSub = PubSub.getInstance<AppEvents>();

// 订阅事件时,参数类型自动推导
pubSub.subscribe('userLogin', (userInfo) => {
  console.log(`用户${userInfo.username}登录,ID:${userInfo.userId}`);
});

// 发布事件时,参数类型会被TypeScript严格检查
pubSub.publish('userLogin', { userId: '123', username: '张三' }); // 类型合法
// pubSub.publish('userLogin', { userId: '123' }); // 类型错误,缺少username字段

核心优化点

  • 通过泛型Events约束事件与参数的对应关系,彻底消除any类型
  • 将事件存储改为数组,支持同一事件的多个订阅者
  • publish和subscribe方法通过泛型推断,确保参数类型与事件完全匹配
  • 新增可选的unsubscribe方法,完善PubSub的核心功能

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 06:30:13