typescript - 是否可以概括一个函数并在打字稿中捕获它的签名?
问题描述
我想创建一个函数,该函数采用一组函数(都具有相同的签名)并返回一个具有相同签名的函数,然后依次运行每个函数。像这样:
function runAll<F extends (...args: any[]) => void>(...funcs: F[]): F {
return ((...args: any[]) => {
funcs.forEach(f => {
f.apply(null, args);
});
}) as F;
}
代码编译并运行良好。但是,如果我接受as F
演员表,打字稿编译器会拒绝以下代码:
Type '(...args: any[]) => void' is not assignable to type 'F'.
'(...args: any[]) => void' is assignable to the constraint of type 'F', but 'F' could be instantiated with a different subtype of constraint '(...args: any[]) => void'.ts(2322)
一般而言,是否可以捕获函数的签名?
解决方案
函数类型扩展与标准类型扩展不同,与标准类型一样,我们有协变,所以我们可以使用给定类型的子类型,对于函数类型,这是参数的逆变和返回类型的协变。
这意味着扩展另一种函数类型的函数类型可以具有与扩展的函数类型相同或更少的参数,并且参数将具有更广泛的类型(逆变)和给定类型的子类型作为回报(协方差)。例如,如果我们有函数(a: "abc", b: number) => string
,那么扩展它的函数就是(a: string) => "abc"
. 确切地说,它的参数较少,并且 return 是字符串的子类型。同样在常识中它是正确的,因为我们可以将缺少参数理解为只是简单地跳过它们。我们甚至可以在这个简单的例子中进行测试:
type F = (a: "abc", b: number) => string
type G = (a: string) => "abc"
type isGExtendsF = G extends F ? true : false; // evaluates into true
F
如果我们将 type 函数传递给它,看看你的泛型是如何表现的() => number
:
runAll(() => 1, () => 2); // F is inferred as () => number
显然() => number
不是我们想要返回的类型(...args: any[]) => void
,它是更窄的类型。
如果我们所有的函数都具有相同的确切参数类型,我从你的函数体中理解,那么我们可以进入以下专注于参数的类型:
function runAll<A extends any[]>(...funcs: ((...args: A) => void)[]): (...args: A) => void {
return ((...args: A) => {
funcs.forEach(f => {
f.apply(null, args);
});
});
}
现在我们阻止使用具有不同类型参数的函数。例如这样的代码:
runAll((a: number) => a, (b: string) => string); // error functions needs to have the same type
推荐阅读
- python - 如何执行启动另一个 python 脚本的 python 宏?
- algorithm - 包依赖的高效算法
- swift - 仅在系统唤醒时触发计时器 (macOS)
- android - 为什么带有填充设置的谷歌地图滞后?
- javascript - JS关闭菜单上的菜单元素单击
- c# - 总是完美方形的平铺 Android CardViews
- php - 为什么 find('a') 会跳过返回下一个路径的主域?
- sas - 如何将 SAS 日期值转换为 2018 年 11 月 20 日至 2018 年 11 月 20 日
- python - 加载视频数据集 (Keras)
- java - 回收站查看项目点击键