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

