经常会有需求对一个对象/类的方法做代理调用,比如如下对象:
const fns = {
say: (msg: string) => msg,
sum: (a: number, b: number) => a + b
}
我们期望通过类似 call('methodName', ...paramas)
的方式实现代理调用,很自然的会写出如下的代码:
type FNS = typeof fns;
function call<T extends keyof FNS>(fn: T, ...args: Parameters<FNS[T]>) {
fns[fn].apply(fns, args)
}
然而,会报如下类型错误:
The 'this' context of type '((msg: string) => string) | ((a: number, b: number) => number)' is not assignable to method's 'this' of type '(this: { say: (msg: string) => string; sum: (a: number, b: number) => number; }, msg: string) => string'.
Type '(a: number, b: number) => number' is not assignable to type '(this: { say: (msg: string) => string; sum: (a: number, b: number) => number; }, msg: string) => string'.(2684)
原因是 fns[fn]
无法与 args
匹配上。
实际上我们在参数定义上的类型声明已经绑定了 fn
与 args
的关系(调用 call 时可正常推断类型了),但在函数实现部分将 fn
倒腾为了 fns[fn]
,对编译器而言,fn
是类型 T extends keyof FNS
,那 fns[fn]
自然就对应了 fns
的所有方法了,无法再与 args
产生一对一的绑定关系。
解决方案如下:
function callFn<T extends (...args: any[]) => any>(f: T, args: Parameters<T>): ReturnType<T> {
return f(...args)
}
function call<T extends keyof FNS>(fn: T, ...args: Parameters<FNS[T]>) {
callFn(fns[fn], args);
}
大佬又更新了