TypeScript编译器无法从联合类型参数中消除筛选的类型
我遇到了TypeScript编译器的问题,如果有人能提供帮助,我将不胜感激 我已经尽可能减少了问题的原始代码,我相信问题的根源来自第二个TypeScript编译器无法从联合类型参数中消除筛选的类型,typescript,Typescript,我遇到了TypeScript编译器的问题,如果有人能提供帮助,我将不胜感激 我已经尽可能减少了问题的原始代码,我相信问题的根源来自第二个if语句 // This code generates a compile error. let val: boolean; function foo(bar: string | string[] | boolean, qux: string[]): void { if ('boolean' === typeof bar) { val
if
语句
// This code generates a compile error.
let val: boolean;
function foo(bar: string | string[] | boolean, qux: string[]): void {
if ('boolean' === typeof bar) {
val = bar;
// At this point TypeScript correctly knows the type of bar is `boolean`
return;
}
// At this point TypeScript correctly knows the type of bar is `string | string[]`
if (!Array.isArray(bar)) {
// Here the compiler understand that `bar` can only be a `string`
bar = [bar];
// This is where things start getting wonky.
//
// Here the compiler thinks `bar` is of type `string | string[] | boolean`
// We have eliminated `boolean` already. We also know that `bar` is not an array because we are
// inside this `if` block.
}
// At this point TypeScript correctly knows the type of bar is `string[]`
val = qux.every(q => bar.includes(q)); // Here the compiler fails.
// semantic error TS2339: Property 'includes' does not exist on type 'false'.
}
有趣的是,通过简单地更改函数的结构,编译器再次感到高兴。在这个函数中,我们提前退出并返回一个值。类型检查是相同的,但是因为我们返回了值,所以编译器正确理解了类型
// This code compiles just fine.
function foo(bar: string | string[] | boolean, qux: string[]): boolean {
if ('boolean' === typeof bar) {
return bar;
}
if (!Array.isArray(bar)) {
return qux.includes(bar);
}
return bar.every(b => qux.includes(b));
}
所以在所有这些之后,我的问题是为什么两者都是
bar=[bar]代码>和val=qx.every(q=>bar.includes(q))代码>有不同的(错误的)类型吗?这有点让人困惑,因为从表面上看,Typescript在lambda中给bar
一个较弱的类型是错误的,并且在这个特定的代码中没有不安全的行为
但是,在一般情况下,Typescript在lambda中为bar
提供较弱的类型是正确的,即使bar
在lambda中使用的同一行上有较强的类型。可通过以下代码演示此问题:
function demo(x: string | number) {
if(typeof x === 'number') {
x = 'now x is a string';
}
// type is string here, so no error
x.includes('now');
// type is string | number here, so this is an error
let f: Function = () => x.includes('now');
x = 23;
// type is number here
f();
}
此代码不是类型安全的,因为尽管在声明函数时,x
确实具有typestring
,但在调用函数时,x
实际上具有typenumber
;因此,没有包含方法。因此,Typescript对代码的抱怨是正确的。但是这个代码的结构和你的一样;您在lambda中使用了一个变量,Typescript不知道稍后该变量具有不同类型时不会调用lambda,因此在lambda的范围内,该变量具有较弱的类型
理论上,Typescript可以被设计为知道数组的every
方法立即调用lambda,然后丢弃它,因此在您的特定代码中,每次调用lambda时,变量肯定是字符串。然而,Typescript的类型系统功能不够强大,无法知道lambda将在什么情况下被调用,如果它被调用,那么它将变得相当复杂
第二个示例没有错误,因为lambda在lambda中调用qux.includes
,而不是bar.includes
。由于变量qux
始终具有类型string[]
,并且没有从较弱的类型缩小,因此这不会导致问题。非常有趣!我唯一仍然不明白的是bar=[bar]是怎么回事
@DanielMorell这只是它显示方式的一个怪癖;紧跟在bar=[bar]之后
,变量bar
的类型正确无误,如果将鼠标悬停在该行的bar
上,它就不会在IntelliSense中显示。但是如果您在下一行写入条
,它将显示为字符串
。隐马尔可夫模型。。。我使用的是WebStorm,如果我在下一行输入bar
,我会得到string | string[]| boolean
。这一定是WebStorm的一个问题。我很惊讶两者之间存在差异,因为两者应该从同一个源(即Typescript实现)获取信息。我不使用WebStorm,所以我恐怕无法进一步调查。