如何在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
相关产品推荐
相关产品推荐

