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

如何为使用arguments的TypeScript方法声明可变参数类型?

兼顾EventEmitter.emit性能与TypeScript类型声明的方案

我有个高频使用的EventEmitter.emit函数,经Babel编译后的基准测试显示,第二个实现版本比第一个快约10%:

// 版本1:性能稍弱
class EventEmitter {
  emit(eventName: string, ...args: any[]) {
    const callbacks = this._callbacks[eventName];
    if (callbacks) {
      for (const cb of callbacks) {
        cb(...args);
      }
    }
  }
}

// 版本2:性能快10%,但TypeScript无法识别参数类型
class EventEmitter {
  emit(eventName: string) {
    const callbacks = this._callbacks[eventName];
    if (callbacks) {
      for (const cb of callbacks) {
        cb(...Array.prototype.slice.call(arguments, 1));
      }
    }
  }
}

现在需要给版本2的emit方法加上(eventName: string, ...args: any[]) => void的类型声明,但又不能让Babel把未使用的...args编译进最终代码里,以下是几种可行方案:

方案1:类型断言绑定函数类型

先定义目标函数类型,再把实现的函数断言为该类型,让TypeScript认可类型匹配:

type EmitFunction = (eventName: string, ...args: any[]) => void;

class EventEmitter {
  private _callbacks: Record<string, Function[]> = {};

  emit: EmitFunction = function(eventName: string) {
    const callbacks = this._callbacks[eventName];
    if (callbacks) {
      for (const cb of callbacks) {
        cb(...Array.prototype.slice.call(arguments, 1));
      }
    }
  };
}

方案2:使用TypeScript函数重载

通过重载签名告知TypeScript方法的参数规范,实际实现则保留高性能版本:

class EventEmitter {
  private _callbacks: Record<string, Function[]> = {};

  // 重载签名:告诉TS这个方法支持任意数量参数
  emit(eventName: string, ...args: any[]): void;
  // 实际实现:无多余参数,保持性能
  emit(eventName: string) {
    const callbacks = this._callbacks[eventName];
    if (callbacks) {
      for (const cb of callbacks) {
        cb(...Array.prototype.slice.call(arguments, 1));
      }
    }
  }
}

方案3:通过Object.assign扩展类原型

在类定义外部给原型添加方法,同时指定类型,避免类内部的语法限制:

class EventEmitter {
  private _callbacks: Record<string, Function[]> = {};
}

type EmitFunction = (eventName: string, ...args: any[]) => void;

Object.assign(EventEmitter.prototype, {
  emit: function(this: EventEmitter, eventName: string) {
    const callbacks = this._callbacks[eventName];
    if (callbacks) {
      for (const cb of callbacks) {
        cb(...Array.prototype.slice.call(arguments, 1));
      }
    }
  } as EmitFunction
});

这些方案的核心逻辑是利用TypeScript类型系统与运行时代码分离的特性:只要让TS认可实现代码符合目标类型,就不需要在运行时保留未使用的...args参数,既满足类型检查需求,又能保留版本2的性能优势。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 11:15:29