Typescript 类型脚本:具有联合类型和三元运算符的类型推断

Typescript 类型脚本:具有联合类型和三元运算符的类型推断,typescript,ternary-operator,union-types,Typescript,Ternary Operator,Union Types,下面的typescript代码编译得很好: let x: 0 | 1 = 0; console.log(x); const r = [true, false]; for (const y of r) { if (y || (x !== 1)) { x = 1; } else { x = 0; } console.log(x); } 但是,该语义等价的代码不: let x: 0 | 1 = 0; console.log(x)

下面的typescript代码编译得很好:

let x: 0 | 1 = 0;

console.log(x);

const r = [true, false];
for (const y of r) {
    if (y || (x !== 1)) {
        x = 1;
    } else {
        x = 0;
    }

    console.log(x);
}
但是,该语义等价的代码不:

let x: 0 | 1 = 0;

console.log(x);

const r = [true, false];
for (const y of r) {
    x = ((y || (x !== 1)) ? 1 : 0);
    console.log(x);
}
x!==1
是:

7:17 error TS2367: This condition will always return 'true' since the types '0' and '1' have no overlap.
在这两种情况下,运行编译后的结果都会产生预期的输出,显示x实际上取了值0和1:

我理解错误源于编译器在第二种情况下将类型
0 | 1
缩小为
0
。但是,仅通过查看代码就可以清楚地看出,
x
可能被指定为1(即使不查看条件)。因此,我希望类型推断假定为最一般的类型,除非明确地告诉我其他情况(如在第一个示例中所做的)。事实上,在第1行中,我明确地告诉编译器我想要更一般的类型:
让x:0 | 1


因此,我的问题是,对于三元运算符,类型推断的行为是否有合理的原因?

错误实际上是因为编译器将
x
的类型缩小为
0

我不确定为什么在另一种情况下你没有得到错误——因为我希望这两种情况下都是一致的——但我没有足够的智慧来编写完成这项工作的工具

x
的类型被强制为
0 | 1
而不是编译器将其缩小为零的示例:

let x = 0 as 0 | 1;

console.log(x);

const r = [true, false];
for (const y of r) {
    x = ((y || (x !== 1)) ? 1 : 0);
    console.log(x);
}

从输出中可以看到,在第一次循环运行后,x取值1。在第二次运行中,条件的计算结果为false(与编译器所说的不同)——这就是为什么x被设置为0。我不认为我会将错误称为“correct”。它更像是众多中的一个;在这种情况下,在。编译器将
x
的类型缩小为
0
,之后又因为将其与
1
进行比较而生气。。。但是当然
0 | 1
应该与
1
相当。通常情况下,狭窄是有帮助的,但在这里它不是。可以通过初始化为
让x=0为0 | 1
或通过比较like
(x!==(1为0 | 1))
来避免这种情况。但为什么在CFA确定x的类型为
0 | 1
的第一种情况下不显示这种权衡呢?这样做毫无意义me@jcalz-编辑以消除歧义-关于“正确”一词的误用,你是对的。我还更新了示例,使其更像一个实际的解决方案。可能我不够清楚:我完全理解错误的原因(将类型
0 | 1
缩小为
0
)。我的论点是,这种行为实际上是不正确的,并且与正常行为不一致:最简单的情况是:
让x:0 | 1=0;x=1
的工作方式很有魅力——编译器检测到在程序运行期间x实际上可以同时获取这两个值,因此正确地限制了范围缩小。一个更复杂的例子是我上面的例子,它也可以很好地工作。我想知道的是,这种异常行为是否有合理的原因,因为如果没有,我认为应该将其标记为bug。
let x = 0 as 0 | 1;

console.log(x);

const r = [true, false];
for (const y of r) {
    x = ((y || (x !== 1)) ? 1 : 0);
    console.log(x);
}