typescript - 打字稿中的函数重载和类型推断
问题描述
我有这种类型:
type Bar = number;
type Foo = {
doIt: ((value: string) => Bar) | ((value: string, provideBar: (bar: Bar) => void) => void);
}
这个想法是Bar
可以根据提供的函数签名以两种方式之一返回。使用实现的代码Foo
如下所示:
function getBar(foo: Foo) {
let bar: Bar = 0;
if (foo.doIt.length === 1) { // We've been provided with the first version of `Foo`
bar = foo.doIt('hello');
// The above line is erroring with:
// - bar: Type 'number | void' is not assignable to type 'number'
// - invocation of "doIt": Expected 2 arguments, but got 1.
} else if (foo.doIt.length === 2) { // We've been provided with the second version of `Foo`
foo.doIt('hello', (_bar) => bar = _bar);
}
}
提供 a 实现的代码Foo
如下所示:
function provideBar() {
const foo1: Foo = {
doIt: (value) => 1. // Error: Parameter 'value' implicitly has an 'any' type.
}
const foo2: Foo = {
doIt: (value, provideBar) => provideBar(2) // Appears to be working
}
}
我希望打字稿有办法表达我想要实现的目标。我不确定为什么会出现这些错误,因为按照我的看法,TS 有足够的信息来阻止能够提供类型推断(我假设 TS 可以function.length
用来区分实现 a 的两种方法Foo
)
解决方案
对于getBar()
's implementation 内部的问题,您遇到了microsoft/TypeScript#18422,在 TypeScript 中列为设计限制。
编译器仅将length
函数的属性视为 type number
,而不是任何特定的数字文字类型,例如1
or 2
。所以检查foo.doIt.length === 1
对控制流分析没有影响,因此编译器不知道它正在调用两种函数类型中的哪一种。
检查的一个主要问题length
是它可能不是您认为的那样。函数可以用剩余参数实现,而且length
很可能是0
。
或者因为 TypeScript 允许您将接受较少参数的函数分配给接受更多参数的函数(请参阅此常见问题解答条目),匹配的函数(value: string, provideBar: (bar: Bar) => void) => void
可能具有length
of1
或0
因为函数实现可以自由地忽略它们的任何输入。
因为这样的怪异length
,TypeScript 基本上什么都不做,建议大家不要尝试这样检查length
。
尽管如此,如果您确信检查会按照您的想法进行(也就是说,没有人会设置"doIt"
为上述“陷阱”版本之一),您可以通过实现用户定义的类型保护函数来获得类似的行为:
function takesOneArg<F extends Function>(x: F): x is Extract<F, (y: any) => any> {
return x.length === 1
}
该takesOneArg()
函数检查length
其类型的类函数参数,如果相等则F
返回,否则返回。返回类型谓词意味着如果是函数类型的联合,则结果意味着可以将类型缩小到只接受一个参数的成员;结果手段可以缩小到其他手段。true
1
false
x is Extract<F, (y: any) => any>
F
true
x
F
false
x
现在您的getBar()
实现按预期工作:
function getBar(foo: Foo) {
let bar: Bar = 0;
if (takesOneArg(foo.doIt)) {
bar = foo.doIt('hello');
} else {
foo.doIt('hello', (_bar) => bar = _bar);
}
}
至于创建一个Foo
在回调参数中看到隐式any
错误的问题,这似乎是microsoft/TypeScript#37580。您希望value
在上下文中键入为string
,但编译器没有这样做。那个 GitHub 问题中没有太多信息,所以我不能说是什么导致了函数联合和上下文类型推断之间的不良交互。
假设这不会很快得到解决,解决方法与我一直推荐的隐式any
问题相同:显式注释编译器无法推断的内容:
const foo1: Foo = {
doIt: (value: string) => 1
}
现在编译没有错误。
推荐阅读
- postgresql - 在 PostgreSQL 中创建汇总表
- android - 如何在不将新版本发布到 Play 商店的情况下更新应用程序的内容
- python - 硒 - text_to_be_present_in_element 和 text_to_be_present_in_element_value 之间的区别
- asp.net-core - ASP.NET Core - 在运行时更改 JWT SecurityKey
- javascript - Gulp-Uglify 错误:意外令牌:名称(Quill)
- android - 垂直和水平对齐的小部件
- php - 通过php中的循环从多维数组中检索值面临的问题
- blogger - 我无法使用博客预览任何内容
- qt - QPainterpath 在运算符 subtraced 中返回 null
- batch-file - 将 *d.dll 复制到调试文件夹,将没有 *d.dll 的 *.dll 复制到 release