在 TypeScript 中,为什么当 noImplicitAny 为 true 时,空数组被推断为“any[]”,而当 noImplicitAny 为 false 时,空数组被推断为“never[]”?

Jor*_*vão 31 typescript

let a = [];
a.push(0);
Run Code Online (Sandbox Code Playgroud)

上面的代码编译得很好

  1. noImplicitAnystrictNullChecks设置为 true
  2. noImplicitAnystrictNullChecks设置为 false

但它会在第二行发出错误

  1. noImplicitAny是假的,strictNullChecks也是真的

发出的错误是Argument of type 'number' is not assignable to parameter of type 'never'.

似乎在前两种情况下a被推断为 a any[],而在最后一种情况下它被推断为 a never[],但为什么会发生这种情况呢?如果noImplicitAny是假的,难道它不应该仍然能够推断出它是吗any[]any[]相反,为什么要像第一种情况那样推断出哪里noImplicitAny是真的?似乎预期的行为以某种方式逆转了......

检查下面所附的图像,了解所使用的错误和选项。

前两种情况: 在此输入图像描述

最后一个案例: 在此输入图像描述

jca*_*alz 26

此问题是在microsoft/TypeScript#36987中提出的。权威的(但不是特别丰富的)答案是这种行为是预期的,并且是向后兼容性所必需的。一般来说,TypeScript 不会引入重大更改,除非这些更改改进的东西比它们破坏的东西多得多。


编译--noImplicitAny器选项编译--strictNullChecks器选项都启用时,空数组并不是真正的类型any[];相反,它们是“自动”或“通配符”或“进化”类型,这些类型根据观察到要添加到其中的值而变化。这是在microsoft/TypeScript#11432中实现的:

let a = []; // auto-typed
a.push(0); // now a is seen as number[]
a.map(x => x.toFixed()) // okay
a.map(x => x.toUpperCase()) // error, numbers don't have a toUpperCase() method
Run Code Online (Sandbox Code Playgroud)

--noImplicitAny关闭时,这种情况不会发生,因为这样做会给以前“很好”的现有代码添加新的错误(如果是a类型any[],那么xmap()方法中应该是类型any,所以在x.toUpperCase()或打字错误之类的x.toFaxed())。


至于--strictNullChecks打开或关闭之间的区别,修复了几个与空数组文字与其他用例交互不良有关的错误;请参阅microsoft/TypeScript#19576microsoft/TypeScript#19745。这些修复更改了打开时的行为--strictNullChecks,但没有更改关闭时的行为,同样是为了在其他用例中保持向后兼容性。


这或多或少就是正在发生的事情。您会看到多个功能和错误修复在某些编译器选项面前以特殊方式相互作用的后果。

务实地说,您应该尽可能使用全套--strict编译器选项。这为您提供了“标准”量的类型安全性,并且被广泛使用,因此社区中有大量的文档和讨论。如果您有选择地禁用某些编译器选项,并且遇到奇怪或意外的情况,那么您可用的资源就会减少,因为您可能是拥有此类配置的极少数人之一。即使你发现了真正的语言错误,修复它的压力也会较小,因此它可能会持续很长一段时间或永​​远。

Playground 代码链接

  • jcalz,你的打字稿答案是如此传奇。谢谢! (5认同)

Rad*_*iță 9

这是预期的行为,可以在此处观察到。

它主要与向后兼容性有关。主要要点是这样的

  • 数组的类型实际上undefined[]被扩展为any[]
  • 但当strictNullChecks为 true 时,则undefined不是一个选项,因此它会更新为never[]