如何在PubSub模式中根据输入参数实现可变回调类型?
解决方案
首先我们需要建立事件名称与对应数据类型的映射关系,让TypeScript能根据订阅/发布的事件名自动推导参数类型,实现类型安全的PubSub。
步骤1:定义事件-数据类型映射
先明确每个事件名称对应的数据结构,用一个类型映射来统一管理:
// 事件名称与对应数据类型的映射,核心类型定义 type NameDataMap = { foo: { a?: { foo: number }, b?: { foo: boolean }, c?: { foo: string } }, bar: { a: { foo: number } // bar事件仅传递a字段,且为必填 }, baz: { b: { foo: boolean }, // baz事件传递b和c字段,均为必填 c: { foo: string } } } // 从映射中自动推导事件名称类型,避免重复定义 type Names = keyof NameDataMap // 针对每个事件的回调类型 type Callback<N extends Names> = (props: NameDataMap[N]) => any
步骤2:重构PubSub类
基于上面的类型映射,修改类的成员和方法,让它们能根据事件名自动匹配类型:
class PubSub { // 订阅者集合:每个事件对应其专属类型的回调数组 subscribers: { [N in Names]: Callback<N>[] } = {} as any subscribe = <N extends Names>(name: N, callback: Callback<N>) => { // 确保订阅者数组存在 if (!Array.isArray(this.subscribers[name])) { this.subscribers[name] = [] } this.subscribers[name].push(callback) } publish = <N extends Names>(name: N, data: NameDataMap[N]) => { const subscribers = this.subscribers[name] subscribers?.forEach(subscriber => subscriber(data)) } }
步骤3:测试类型约束
现在使用PubSub时,TypeScript会自动根据事件名校验参数类型:
const ps = new PubSub() // 订阅bar事件:回调仅能接收{ a: { foo: number } },访问其他字段会报错 ps.subscribe('bar', ({ a }) => console.log(a.foo)) // ❌ 错误示例:如果尝试访问b,TypeScript会提示“属性b不存在于类型{ a: { foo: number } }” // ps.subscribe('bar', ({ b }) => console.log(b.foo)) // 订阅foo事件:回调能接收全部字段 ps.subscribe('foo', ({ a, b, c }) => { a?.foo && console.log(a.foo) b?.foo && console.log(b.foo) c?.foo && console.log(c.foo) }) // 订阅baz事件:回调仅能接收{ b: { foo: boolean }, c: { foo: string } } ps.subscribe('baz', ({ b, c }) => { console.log(b.foo, c.foo) }) // 发布事件时,数据类型也会自动匹配 ps.publish('bar', { a: { foo: 0 } }) // 正确 // ❌ 错误示例:bar事件不能传递b字段 // ps.publish('bar', { b: { foo: true } })
关于额外需求的说明
通过定义NameDataMap这个核心类型映射,subscribe和publish方法可以共享同一套类型规则,不需要手动为每个方法重复编写类型判断。只要修改NameDataMap,所有相关方法的类型约束会自动同步,实现了“智能类型识别”的效果。
内容的提问来源于stack exchange,提问作者chitzui
相关产品推荐
相关产品推荐

