这是TypeScript的一个限制.对于泛型函数或重载函数,对强类型Function.prototype.call()
methods(即introduced in TypeScript 3.2)的支持不起作用.参见microsoft/TypeScript#27028中的this comment,实现该支持的pull请求.
当您直接调用重载函数时,编译器将 Select 最合适的调用签名.但是当你开始处理重载函数types时,编译器基本上会放弃,只 Select 其中一个调用签名,而不注意它是否适合最终的用例.这通常是最后一个通话信号(尽管有时我猜是第一个)它没有很好的记录;有关信息,请参见microsoft/TypeScript#43187.
在您的例子中,这意味着_originalMove
上的Function.prototype.call()
的行为就好像它只有最后一个(target: string) => void;
类型的呼叫签名一样.这对你的一个电话很好,但对另一个电话不好.哦,好吧.
这里的一个解决方法是,在调用call()
之前,显式地将_originalMove
的类型扩展到单个相关的调用签名.也就是说,以下被视为安全任务:
const om: (direction: number) => void = _originalMove;
因为_originalMove
有两个电话签名.此时,om.call()
将允许第二个参数为number
类型:
return om.call(this, direction);
一切正常:
class CreepExtra extends Creep {
move(direction: number): void;
move(target: string): void;
move(direction: number | string) {
if (typeof direction === 'string') {
return _originalMove.call(this, direction);
}
const om: (direction: number) => void = _originalMove;
return om.call(this, direction); // okay
}
}
万岁!
请注意:
在这里显示的示例中,重载方法有两个调用签名,它们只不同于单个函数参数的类型;这样的呼叫签名可以通过union types轻松统一:
declare class Creep {
move(directionOrTarget: number | string): void;
}
const _originalMove = Creep.prototype.move;
class CreepExtra extends Creep {
move(targetOrDirection: number | string) {
return _originalMove.call(this, targetOrDirection);
}
}
因为重载和类型推断并不能很好地结合在一起,如本答案的第一部分所示,如果你有机会在不牺牲任何其他东西的情况下删除重载,那么你应该接受它.您的实际用例可能不像这里显示的示例代码那么简单,但对于每个人来说,判断重载的好处是否大于缺点是一个很好的练习.
Playground link to code