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

使用TypeScript映射类型约束发布订阅类方法类型

实现带类型约束的TypeScript发布订阅类

这问题我之前也碰到过,核心就是用泛型把事件类型和对应的数据类型绑定起来,让TypeScript能自动做类型校验。下面给你一个完整的可运行方案:

1. 定义泛型发布订阅接口

首先修正你的EventPublisher泛型接口,用索引类型和泛型参数来关联事件类型与数据类型:

// 先定义你的事件类型集合
type EventType = "A" | "B" | "C";

// 泛型接口:U是事件到数据类型的映射,约束为EventType的部分或全部键对应的数据类型
interface EventPublisher<U extends Partial<Record<EventType, unknown>>> {
  // 订阅方法:T是具体的事件类型,回调参数类型自动关联U[T]
  subscribe<T extends keyof U>(eventType: T, callback: (data: U[T]) => void): void;
  // 发布方法:同样用T关联事件类型和数据类型
  publish<T extends keyof U>(eventType: T, data: U[T]): void;
}

这里用Partial<Record<EventType, unknown>>是为了允许你只定义部分事件对应的数据类型(比如你示例里的X只定义了"A"和"B"),如果要求必须覆盖所有EventType,可以去掉Partial。

2. 实现具体的发布订阅类

接下来实现这个接口,内部维护一个按事件类型分类的回调数组:

class ConcreteEventPublisher<U extends Partial<Record<EventType, unknown>>> implements EventPublisher<U> {
  // 订阅者存储:每个事件类型对应一组回调,类型严格匹配
  private subscribers: { [K in keyof U]?: Array<(data: U[K]) => void> } = {};

  subscribe<T extends keyof U>(eventType: T, callback: (data: U[T]) => void): void {
    // 如果该事件还没有订阅者数组,初始化一个
    if (!this.subscribers[eventType]) {
      this.subscribers[eventType] = [];
    }
    // 添加回调到对应数组
    this.subscribers[eventType]!.push(callback);
  }

  publish<T extends keyof U>(eventType: T, data: U[T]): void {
    // 获取该事件的所有回调,遍历执行
    const callbacks = this.subscribers[eventType];
    if (callbacks) {
      callbacks.forEach(callback => callback(data));
    }
  }
}

3. 测试使用

按照你示例里的X接口来测试,类型校验完全生效:

interface X {
  "A": number;
  "B": string;
  // 这里可以不用定义"C",因为我们用了Partial约束
}

// 创建实例
const publisher: EventPublisher<X> = new ConcreteEventPublisher<X>();

// 正确的发布:类型匹配
publisher.publish("A", 1); 

// 错误的发布:类型不匹配,TypeScript会报错
publisher.publish("A", "blah"); 

// 订阅时,回调参数自动推断为对应类型
publisher.subscribe("B", (str) => {
  console.log(str.toUpperCase()); // str被推断为string,编辑器会有自动提示
});

关键知识点解释

  • 泛型约束:U extends Partial<Record<EventType, unknown>>确保U的键只能是EventType里的成员,值是任意类型(unknown比any更安全)。
  • 索引类型:T extends keyof U让方法的泛型参数T绑定到U的具体键,从而让U[T]自动对应该事件的数据类型。
  • 严格类型存储:subscribers对象的类型定义确保每个事件类型的回调数组只能接收对应数据类型的回调函数,从根源上避免类型不匹配。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 04:02:50