Typescript - 错误推断 'never'

Typescript - Incorrectly inferring 'never'

这是一个基本用例:用 null 初始化变量然后更改某些嵌套 loop/function:

中的值
let a: number | null = null;
[1].forEach(() => {
  a = 1;
});

if (a != null)
  a.toFixed(); // Error: Property 'toFixed' does not exist on type 'never'.

然而打字稿将 a 的类型推断为 never。我会假设如果没有 if 它会假设它是 null | number 在这种情况下我可能会得到一个错误,指出 属性 在 null 上不存在,但为什么它假设它永远不要仅基于初始分配值。

我是不是做错了什么?

如果您完全确定 a 在那里有一个值,那么您可以将 ! 放在变量

之后
let a: number | null = null;
[1].forEach(() => {
  a = 1;
});

if (a !== null)
  a!.toFixed(); //

我不会用null,但undefined,所以没必要用!

let a: number | undefined;
[1].forEach(() => {
  a = 1;
});

if (a) { // <-- if undefined or 0
  a.toFixed(); // No problem here
}

也建议使用 !== 而不是 !=

派对迟到了,但这是我的 2 美分。

对已接受答案的备注

if (a) {
  a.toFixed(); // No problem here
}

请注意,当 a0 时,if 块将 不会 被调用。

  • 要解决此问题,请使用 if (a !== undefined)
  • 否则(当您真的不想处理 0 时,您最好将 a 初始化为 0,如下所示:
    let a = 0; // typescript will infer the type number
    ...
    if (a) {
      // a is of type number and !== 0
    }

回复评论

Why would you initialize a variable using undefined?

人们有时会这样做,因为一些工具(IDE、linters、..)报告 errors/warnings 否则。

例如当您使用具有默认打字稿设置的 IntelliJ IDEA​​ 时,这是一个警告:

我建议停用这些检查,因为 javascript 中未初始化的变量始终具有值 undefined:即在某些其他语言(即 C)中,变量可能有一些随机的“垃圾” " 值。

引自MDN: Global_Objects/undefined#description

A variable that has not been assigned a value is of type undefined.

对于所有其他值(即 不是 undefined 的值),打字稿编译器将显示错误:
TS2454: Variable 'xxx' is used before being assigned.

原问题的答案

let a: number | null = null;
[1].forEach(() => {
  a = 1;
});

if (a != null)
  a.toFixed(); // Error: Property 'toFixed' does not exist on type 'never'.

这仅在编译器选项 strictNullChecks 启用时发生。

这句话很好地说明了原因(Quote Reference)

While strictNullChecks implies that it is just checking for usage of variables that might be undefined or null it really turns the compiler into a very pessimistic mode, where when there are no contextual way of inferring the type, it will choose the narrowest type, instead of the widest type,

这意味着,详细地说:

  • 由于打字稿编译器不够聪明,无法知道是否调用了 forEach 循环(因此分配了一个值),因此它采用悲观的方法并假设 x 仍然是 null
  • 因此,循环后x的类型是null(不是我们预期的number | null
  • 现在,最后的 if 块检查 if x !=== null 永远不会是这种情况(因为打字稿假定 x null 当 if-语句被执行。因此if语句中x的类型是never
  • 所以一个“解决方案”是明确告诉打字稿你确定 x 的值是通过使用 x!.toFixed()
  • 定义的

其他

strictNullChecks

strictNullChecks 关闭时,代码有效:TypeScript example: strictNullChecks=off
我强烈建议不要那样做。

for..of 循环

当您使用 for..of 循环而不是 forEach() 时,即使 strictNullChecks 处于打开状态,代码也能正常工作:Playground

let a: number | null = null;
for (const i of [1]) {
  a = 1;
};
if (a != null)
  a.toFixed();

其他初始值

你也可以考虑其他初始化值(而不是undefinednull):Playground

let a = 0; // typescript will infer that a is of type number
[1].forEach(() => {
  a = 1;
});
if (a >= 0)
  a.toFixed();


let b = NaN; // typescript will infer that b is of type number
[1].forEach(() => {
  a = 1;
});
if (!isNaN(b))
  b.toFixed();