【发布时间】:2020-05-01 20:56:36
【问题描述】:
我想定义一个函数,它可以接受以两种方式之一输入的参数。例如:
type Fn = {
(abc: number, def: string): void,
(abc: string): void,
};
给定这个类型签名,如果abc是一个数字,那么def是一个字符串,如果abc是一个字符串,那么def是没有定义的。这对人类来说是很清楚的,但是 Typescript 有没有办法识别它呢?例如,以下实现失败:
const fn: Fn = (abc: number | string, def?: string) => {
if (typeof abc === 'string') console.log(abc.includes('substr'));
else console.log(def.includes('substr'));
}
因为虽然abc的类型已经缩小,但是TS不理解def的类型也已经确定,所以def.includes是不允许的。函数的调用者可以识别参数类型的分组,因此正如预期的那样,以下是禁止的:
fn('abc', 'def');
但是重载的类型分组在函数内部似乎没有任何作用。
当只有几个参数时,很容易显式(且冗余地)对每个参数进行类型检查,或者在检查完一个参数后对每个参数使用类型断言,但这仍然很难看。当参数超过几个时,情况会变得更糟。
另一个有问题的冗余是每个可能的参数类型不仅需要在type 中列出,还需要在函数的参数列表中列出。例如,类型定义中的(abc: number) 和(abc: string)也需要参数列表中的= (abc: number | string)。
是否有更好的模式可用于函数重载而不完全放弃它?我知道至少有两种不涉及重载的解决方法:
传递
{ abc: number, def: string } | { abc: string }类型的对象而不是多个单独的参数,然后通过类型保护传递对象对两种不同类型的参数使用两个单独的函数
但我宁愿使用重载如果有一个不错的方法来处理它。
【问题讨论】: