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

TypeScript+React实现类Vue EventBus时spread参数报错解决

TypeScript 实现类型安全的Vue风格EventBus

错误根源

原代码的类型报错和ESLint问题来自两点:

  • 初始定义的回调类型是无入参函数() => void,和$emit时展开传参的行为类型不匹配
  • 后续修改的(params: Array<string | number>) => void要求回调必须接收单个数组入参,和展开传参的逻辑不符;如果回调内没有使用这个形参,自然会触发no-unused-vars规则。

可直接使用的实现代码

// 定义支持任意个入参的回调类型,使用剩余参数语法匹配展开传参
type EventCallback = (...args: Array<string | number | boolean | object | null | undefined>) => void

class _EventBus {
  // 用Map存储事件回调,相比普通对象更适合频繁增删场景,避免原型链键名冲突
  private readonly bus: Map<string | number, EventCallback>

  constructor() {
    this.bus = new Map()
  }

  /**
   * 解绑指定事件
   * @param id 事件唯一标识
   */
  $off(id: string | number) {
    this.bus.delete(id)
  }

  /**
   * 注册事件监听
   * @param id 事件唯一标识
   * @param callback 事件触发时执行的回调
   */
  $on(id: string | number, callback: EventCallback) {
    this.bus.set(id, callback)
  }

  /**
   * 触发事件
   * @param id 事件唯一标识
   * @param args 传给回调的任意个参数
   */
  $emit(id: string | number, ...args: Parameters<EventCallback>) {
    const callback = this.bus.get(id)
    if (callback) {
      callback(...args)
    }
  }

  /**
   * 注册只触发一次的事件监听
   * @param id 事件唯一标识
   * @param callback 事件触发时执行的回调
   */
  $once(id: string | number, callback: EventCallback) {
    const onceWrapper: EventCallback = (...args) => {
      callback(...args)
      this.$off(id)
    }
    this.bus.set(id, onceWrapper)
  }
}

export const EventBus = new _EventBus()

关键说明

  • 回调类型使用剩余参数语法定义,既匹配$emit时的展开传参逻辑,也兼容任意参数个数的回调传入:不需要参数的回调可以直接写() => {},只需要部分参数的回调可以只声明用到的形参,不会触发no-unused-vars报错。
  • 存储结构替换为Map,相比普通对象在频繁增删键值对的场景性能更好,也不会出现对象原型属性导致的键名冲突问题。
  • $emit调整为剩余参数传参形式,调用方式和Vue原生EventBus完全一致,不需要手动把参数包成数组:
    // 调用示例
    EventBus.$emit('user:login', 1001, '张三', { role: 'admin' })
    

React场景使用注意

事件监听必须在组件卸载时解绑,避免内存泄漏:

import { useEffect } from 'react'
import { EventBus } from './event-bus'

export default function UserPanel() {
  useEffect(() => {
    // 注册监听
    const handleLogin = (userId: number, userName: string) => {
      console.log('用户登录:', userId, userName)
    }
    EventBus.$on('user:login', handleLogin)

    // 组件卸载时解绑
    return () => {
      EventBus.$off('user:login')
    }
  }, [])

  return <div>用户面板</div>
}

如果需要更严格的类型约束,可以基于泛型扩展事件名和参数的映射关系,实现事件名、参数类型的自动校验,避免传错参数。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.01 05:33:23