Typescript - 错误地推断“从不”

问题描述 投票:0回答:2

这是一个基本用例:用 null 初始化变量,然后更改某些嵌套循环/函数中的值:

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 上不存在,但为什么它假设它永远不会仅基于初始分配值。

我做错了什么吗?

typescript type-inference
2个回答
7
投票

如果您绝对确定

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
}

也建议使用

!==
而不是
!=


6
投票

聚会迟到了,但这是我的 2 美分。

对已接受的答案进行备注

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

请注意,当 a

0
时,不会调用 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 }
回复评论

为什么要使用 undefined 来初始化变量?

人们有时会这样做,因为某些工具(IDE,linter,..)否则会报告错误/警告。

例如当您使用 IntelliJ IDEA 和默认的 typescript 设置时,这是一个警告:


enter image description here 我建议停用这些检查,因为 javascript 中未初始化的变量始终具有值

undefined

:即在某些其他语言(即 C)中,该变量可能具有一些随机“垃圾”值。

引用自

MDN:Global_Objects/undefined#description

未赋值的变量的类型为未定义。

对于所有其他值(即
not

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

打开时才会发生这种情况。

这句话很好地描述了原因(

引用参考

虽然 strictNullChecks 意味着它只是检查可能未定义或 null 的变量的使用情况,但它确实将编译器转变为非常悲观的模式,当没有上下文方式推断类型时,它会选择最窄的类型,而不是最宽的类型,

详细来说,这意味着:

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

严格空检查

strictNullChecks

关闭时,代码可以工作:

TypeScript 示例: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();

其他初始值

您还可以考虑其他初始化值(而不是

undefined

null
):
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();

	
© www.soinside.com 2019 - 2024. All rights reserved.