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

如何用TypeScript实现按函数名的类型安全方法包装器?

类型安全的TypeScript方法包装器实现需求

我已经看过不少关于TypeScript函数包装器的问答,但想知道如何实现针对命名方法的类型安全包装器。我希望能写出类似以下JavaScript形式的代码,同时让TypeScript能根据方法名自动检查包装函数的参数类型是否匹配:

单个方法包装示例(JavaScript)

function wrap(API, fnName, fn) {
  const origFn = API.prototype[fnName];
  API.prototype[fnName] = function(...args) {
    fn(...args);
    return origFn.call(this, ...args);
  };
}

wrap(CanvasRenderingContext2D, 'fillRect', function(x, y, w, h) {
  console.log(x, y, w, h);
});

// 验证功能
const ctx = document.querySelector('canvas').getContext('2d');
ctx.fillRect(10, 20, 100, 40);
<canvas></canvas>

批量方法包装示例(JavaScript)

function wrap(API, fnName, fn) {
  const origFn = API.prototype[fnName];
  API.prototype[fnName] = function(...args) {
    fn(...args);
    return origFn.call(this, ...args);
  };
}

const wrappers = {
  fillRect(x, y, w, h) {
    console.log('fill:', x, y, w, h);
  },
  strokeRect(x, y, w, h) {
    console.log('stroke:', x, y, w, h);
  },
};

for (const [name, fn] of Object.entries(wrappers)) {
  wrap(CanvasRenderingContext2D, name, fn);
}

// 验证功能
const ctx = document.querySelector('canvas').getContext('2d');
ctx.fillRect(10, 20, 100, 40);
ctx.strokeRect(40, 70, 20, 40);
<canvas></canvas>

请问能否用TypeScript实现上述两种形式的类型安全版本?实际场景中包装函数会执行更多逻辑,必须确保类型完全匹配。


类型安全的TypeScript实现方案

以下是针对单个方法和批量方法的类型安全包装器实现,利用TypeScript泛型与索引类型确保包装函数与原方法的类型严格匹配:

1. 单个方法的类型安全包装器

type Constructor<T = {}> = new (...args: any[]) => T;

function wrap<T extends Constructor, K extends keyof InstanceType<T>>(
  API: T,
  fnName: K,
  fn: (...args: Parameters<InstanceType<T>[K]>) => void
) {
  const origFn = API.prototype[fnName] as InstanceType<T>[K];
  API.prototype[fnName] = function(this: InstanceType<T>, ...args: Parameters<InstanceType<T>[K]>) {
    fn(...args);
    return origFn.call(this, ...args);
  } as InstanceType<T>[K];
}

// 使用示例
wrap(CanvasRenderingContext2D, 'fillRect', (x, y, w, h) => {
  console.log(x, y, w, h);
  // 若参数类型错误(比如将x传入字符串),TypeScript会直接抛出编译错误
});

// 验证功能
const ctx = document.querySelector('canvas')?.getContext('2d');
ctx?.fillRect(10, 20, 100, 40);

类型约束说明

  • Constructor<T>:限定传入的API必须是可实例化的构造函数。
  • K extends keyof InstanceType<T>:确保fnName是API实例的有效方法名。
  • Parameters<InstanceType<T>[K]>:自动提取原方法的参数类型,强制包装函数的参数与原方法完全对齐。

2. 批量方法的类型安全包装器

扩展实现批量包装功能,确保包装对象的每个方法都与原API方法类型匹配:

type Constructor<T = {}> = new (...args: any[]) => T;

// 复用单个包装函数
function wrap<T extends Constructor, K extends keyof InstanceType<T>>(
  API: T,
  fnName: K,
  fn: (...args: Parameters<InstanceType<T>[K]>) => void
) {
  const origFn = API.prototype[fnName] as InstanceType<T>[K];
  API.prototype[fnName] = function(this: InstanceType<T>, ...args: Parameters<InstanceType<T>[K]>) {
    fn(...args);
    return origFn.call(this, ...args);
  } as InstanceType<T>[K];
}

// 批量包装函数
function wrapBatch<T extends Constructor>(
  API: T,
  wrappers: {
    [K in keyof InstanceType<T>]?: (...args: Parameters<InstanceType<T>[K]>) => void
  }
) {
  for (const [name, fn] of Object.entries(wrappers) as [keyof InstanceType<T>, any][]) {
    fn && wrap(API, name, fn);
  }
}

// 使用示例
wrapBatch(CanvasRenderingContext2D, {
  fillRect(x, y, w, h) {
    console.log('fill:', x, y, w, h);
  },
  strokeRect(x, y, w, h) {
    console.log('stroke:', x, y, w, h);
  },
  // 若传入不存在的方法名或参数类型错误,TypeScript会立即报错
});

// 验证功能
const ctx = document.querySelector('canvas')?.getContext('2d');
ctx?.fillRect(10, 20, 100, 40);
ctx?.strokeRect(40, 70, 20, 40);

批量包装类型说明

  • wrappers参数使用映射类型,确保对象键为API实例的有效方法名,值为对应参数类型的函数。
  • 通过类型断言修正Object.entries的返回类型,避免循环中丢失方法名的类型信息。

核心优势

  • 编译期类型检查:参数不匹配、方法名不存在等问题会在编译阶段暴露,避免运行时错误。
  • 自动类型推导:无需手动声明参数类型,TypeScript会根据原方法自动推导。
  • 保留this上下文:包装后的方法仍能正确绑定原实例的this,不影响原方法逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 09:05:43