理论
What are Function overloads in Typescript
函数重载允许我们为同一个函数指定多个签名——例如,同一个函数被调用并返回 a ,但如果它被调用则返回 a 。strings
string
numbers
number
考虑到 javascript 是一种动态语言,我们有时会遇到一个函数可以接受不同类型的值并
返回不同类型的值的情况。比方说,您正在验证用户输入——使用邮递员的人可以发送他们想要的任何值——然后由您决定如何验证它。
在这种情况下,我们想告诉打字稿——嘿,
可以用类型 A、B 和 C 调用完全相同的函数,但我仍然想利用类型检查,因为根据调用函数的类型,我可以
得出结论函数的返回类型。
解决方案是为同一个函数提供多个函数类型作为重载列表,在代码中:
function reverse(a: string): string; function reverse(a: string[]): string[]; function reverse(a: string | string[]): string | string[] { // your implementation }
在上面的代码片段中,我们有一个reverse
函数,它可以反转一个string
或一个array
字符串。根据传入的输入,它将返回相同的类型但相反。
通过键入每个可能的场景,我们帮助打字稿在我们调用它时推断函数的返回值。
如果我们不指定可能的场景,typescript 无法知道返回值,即使调用者看起来很明显:
function printSum(a: string | number, b: string | number): string | number { if (typeof a === 'string' || typeof b === 'string') { return String(a) + String(b); } // At this point we know that both a and b are numbers return a + b; } // number is of type string | number const number = printSum(10, 5);
我们知道通过将两个数字传递给printSum
我们会得到一个数字,但是打字稿无法知道,所以我们得到一个联合返回类型
string | number
.
完整示例
type Comparable = Record<string, string> | string[]; function getLongerValue(a: Comparable, b: Comparable): Comparable { if (Array.isArray(a) && Array.isArray(b)) { return a.length > b.length ? a : b; } // we assume they passed objects because these are the only use cases // we intend to handle if (!('length' in a) && !('length' in b)) { return Object.keys(a).length > Object.keys(b).length ? a : b; } throw new Error("You didn't pass in arrays or objects"); } // ["hello", "world"] // notice longerArray is of type Comparable // even tho we know that by passing in 2 arrays we get an array back const longerArray = getLongerValue(['hello'], ['hello', 'world']); console.log(longerArray); // {name: 'Tom', profession: 'programmer'} // notice longerObject is of type Comparable // even tho we know that by passing in 2 objects we get an object back const longerObject = getLongerValue( {name: 'Tom'}, {name: 'Tom', profession: 'programmer'}, ); console.log(longerObject); // Error: You didn't pass in arrays or objects // ideally we would like to inform the user at dev time rather than runtime // that they shouldn't call this function with mixed inputs const longerMixed = getLongerValue(['hello'], { name: 'Tom', profession: 'programmer', }); console.log(longerMixed);
在上面的示例中,我们注意到一些缺点 – 我们没有为 typescript 提供帮助我们进行类型检查所需的帮助。
通过传入 2 个数组,比较它们的长度并返回更长的数组,我们可以确定,如果我们使用 2 个数组调用函数,我们将返回一个数组,但是对于我们当前的实现,我们将返回一个对象或数组类型。
让我们添加函数重载以帮助打字稿帮助我们:
function getLongerValue( a: Record<string, string>, b: Record<string, string>, ): Record<string, string>; function getLongerValue(a: string[], b: string[]): string[]; function getLongerValue(a: Comparable, b: Comparable): Comparable { // ... rest of the implementation }
我们告诉打字稿 – 该getLongerValue
函数可以通过以下方式调用:
- 两个对象,它将返回一个对象
- 两个数组,它将返回一个数组
结果是更好的全面类型检查,如果我们现在将鼠标悬停在
longerArray
变量上,我们可以看到它的类型string[]
。
我们还在分配
longerMixed
变量的调用点看到一个错误 – 错误基本上指出:
嘿,您已指定使用 2 个类型为 string[] 的元素或 2 个类型为 Record<string, string> 的元素调用该函数 – 您两者都没有做,因此这种行为不是预期的。
通过使用函数重载,我们可以利用 javascript 语言的动态特性,同时仍然使用类型检查。
在某些情况下,一个函数对其参数类型有不止一个签名,并且通常我们在调用时根据我们传入的参数类型知道这些函数的返回类型。
通过重载采用不同类型参数的函数,我们在调用时获得类型检查参数以及类型化返回值。
Typescript 从上到下检查过载兼容性。它检查第一个重载,尝试使用提供的参数调用函数。如果类型匹配,它会选择该重载作为正确的重载,否则它会继续下一个重载。
这就是我们将重载从最具体到最不具体的顺序排序的原因。
请注意,函数实现不是重载列表的一部分,因此我们的getLongerValue
函数只有 2 个重载。string[]
如果我们使用or以外的任何参数调用我们的函数,Record<string, string>
我们将得到一个错误。