精明的蚂蚁 · v-model 调用函数怎么传参 - CSDN文库· 1 周前 · |
帅气的蚂蚁 · C#动态生成枚举类型 - CSDN文库· 1 周前 · |
聪明的冰棍 · 微信小程序ios和安卓扫码踩坑记(扫码时on ...· 13 小时前 · |
考研的猕猴桃 · iText 7 html2pdf ...· 9 月前 · |
很酷的山楂 · Python中计算图像亮度_python获取 ...· 1 年前 · |
坏坏的蟠桃 · 优化网站设计(二十一):尽量少用iframe ...· 1 年前 · |
酒量大的蛋挞 · 解决table边框在打印中不显示的问题 - ...· 1 年前 · |
博学的乌龙茶 · MySQL查看用户权限· 1 年前 · |
考虑以下片段:
type Add = (a: number | string, b: number | string) => number | string;
function hof(cb: Add) {}
const addNum = (a: number, b: number): number => a + b;
const addStr = (a: string, b: string): string => a + b;
// @ts-expect-error
hof(addNum);
// @ts-expect-error
hof(addStr);
为什么我们不能将
addNum
和
addStr
函数传递给
hof
,海事组织他们的呼叫签名应该与
hof
所期望的兼容,但实际上并非如此。
如果它们的类型是不兼容的,那么为什么下面的代码片段中的重载签名没有抱怨呢?
function add(a: number, b: number): number;
function add(a: string, b: string): string;
function add(a: number | string, b: number | string): number | string {
return 1;
}
TL;DR:
hof
需要回调来覆盖比这两个函数单独处理的更多的情况。
在这个案子里你搞错了。
hof
指定将用两个参数调用其回调,其中一个参数可以是字符串,也可以是数字。这意味着一个只能处理两个数字参数的函数是不够的(因为它也可能被传递一个字符串)。
所以,另一种方法会起作用:
function hof(cb: (a: number, b: number) => number) {}
function add(a: number | string, b: number | string): number | string {
return 1;
hof(add);
因为在这种情况下,add保证能够处理传递给它的两个数字(至少基于它的签名)。
那么为什么重载签名会起作用呢?因为上下文是完全不同的。作为函数的重载,它们是完全正确的,因为它们是更一般函数的更具体的签名。但这是函数参数的错误方向,您需要传递一个参数与函数 或较小的 所要求的参数相同的函数。您传递了一个具有更具体参数的函数。
您假设是协变类型,但函数参数是相反的。(有关这方面的一些内容,请参见 这篇文章 。)
将您的示例稍微更改一下,您就会发现问题所在:
type Add = (a: number | string, b: number | string) => number | string;
function hof(cb: Add) {
return cb
const addNum = (a: number, b: number): number => Math.round(a) + Math.round(b);
const addStr = (a: string, b: string): string => `${a.toLowerCase()}${b.toLowerCase}`;
hof(addNum)('a', 'b'); // should error because addNum can't take strings
hof(addStr)(123, 456); // should error because addStr can't take numbers
这里,
hof
返回传递它的回调函数。因此,如果您传递它
addNum
,那么字符串就会崩溃。如果你把它传给
addStr
,那么数字就会崩溃。
因此,若要将函数分配给函数类型,则必须接受所有参数类型的超集,而不是子集。
但是,如果将
hof
设置为泛型,则可以根据参数创建函数类型。例如:
type Add<T extends number | string> = (a: T, b: T) => T;
function hof<T extends number | string>(cb: Add<T>): Add<T> {
return cb
const addNum = (a: number, b: number): number => Math.round(a) + Math.round(b);
const addStr = (a: string, b: string): string => `${a.toLowerCase()}${b.toLowerCase}`;
hof(addNum)(123, 456); // fine
hof(addStr)('a', 'B'); // fine
hof(addNum)('a', 'b'); // should error because addNum can't take strings
hof(addStr)(123, 456); // should error because addStr can't take numbers
要了解这里发生了什么,我们需要了解
hof
函数允许使用传递给它的
cb
的方式。
什么论据可以通过?
让我们考虑一个简单的例子,只包含一个参数。
function hof(cb: (a: number | string) => number | string) {}
因此,
hof
表示它需要一个回调函数
cb
,它接受一个可以是数字或字符串的参数,并返回一个字符串或一个数字。
现在,考虑一下TS在
hof
上可以传递给
cb
的限制。因此,
hof
可以使用
number
或
string
调用
cb
,这是可以的,因为
hof
已经声明它接收到的
cb
应该能够同时处理数字和字符串。
但是,如果将以下
double
函数传递给
hof
,TS会发出抱怨:
function double(a: number): number {
return 2 * a
}
这是有意义的,因为
hof
可以用
string
调用这个
double
函数,这可能会破坏
double
函数,因为它只接受数字。
还请注意抛出错误的位置,它是在用
double
调用
double
的地方抛出的,这表明
hof
的调用方式有问题,而不是如何实现它。
function hof(cb: (a: number | string) => number | string) {}
function double(a: number): number {
return 2 * a
// @ts-expect-error
hof(double)
因此,传递给
hof
hof
的回调应该能够处理允许
hof
传递给回调的一组参数。
如何使用返回类型?
现在,让我们看看
hof
如何使用
cb
返回的值。
因为
cb
可以返回一个
number
或一个
string
,所以
hof
有责任明智地使用返回值,这意味着
hof
不应该假设返回的值总是数字或字符串,
hof
需要在做出任何这样的假设之前设置适当的类型保护。
function hof(cb: (a: number | string) => number | string) {
// @ts-expect-error
const fixed = cb(100).toFixed() // wrong assumption that the returned value is always a number
function hof(cb: (a: number | string) => number | string) {
const ret = cb(100)
if (typeof ret === "number") { // appropriate check to make sure that it's a number
const fixed = ret.toFixed();
}
再次注意抛出错误的位置,它是在函数体中抛出的,这表明函数的实现存在问题。
传递给
hof
的回调也可以返回一个
number
,TS对此绝对没有问题。
function hof(cb: (a: number | string) => number | string) {}
精明的蚂蚁 · v-model 调用函数怎么传参 - CSDN文库 1 周前 |
帅气的蚂蚁 · C#动态生成枚举类型 - CSDN文库 1 周前 |
博学的乌龙茶 · MySQL查看用户权限 1 年前 |