TypeScript严格空值检查 - 数组访问怎么办?

15

如果启用了TypeScript的严格空值检查,我希望编译器阻止我将nullundefined值分配给一个变量,除非它接受null

然而,数组访问似乎允许绕过这个检查。

例如:

let a: string[] = ["Hello"];
let s: string;

// 1) this produces an error, as expected
s = undefined

// 2) s is undefined here, too, but no error
s = a[3];
console.log(s);
在 TypeScript Playground 上运行此示例 (注意:“选项”对话框中必须启用“严格的空检查”)。
这里发生了什么?
  • 这是 TypeScript 编译器的错误吗?
  • 还是故意省略了某些内容?
  • 如果是后者,是否有文档记录(最好附带理由)?

1
请查看此链接:https://github.com/danielnixon/eslint-plugin-total-functions - danielnixon
@danielnixon:有趣 - 显然我不是唯一注意到这个的人。 - sleske
@danielnixon:考虑根据你的项目编写一个答案。在我看来,如果你想避免使用数组,使用你的插件似乎是一个有效的解决方案(或至少是一种变通方法)。 - sleske
2个回答

18

找到了 :-).

tl;dr: 这是一种有意的省略。在 TypeScript 代码中,数组访问非常常见,并且为每次访问强制进行 null/undefined 检查被认为对开发人员来说太麻烦了。

请注意,从 TypeScript 4.1 开始,可以使用编译器选项 noUncheckedIndexedAccess 进行配置。有关详细信息,请参见 Klaster_1 的答案。


这个问题在讨论中已经提出了几次:

在 PR 7140 上的评论中,核心开发人员之一的 Anders Hejlsberg 有一个不错的理由解释:

索引只是生成与匹配索引签名中声明的类型相同的值。即使在技术上更加正确,如果我们自动将 undefined 添加到每个索引操作的类型中,那将会非常痛苦。

例如,每个数组元素访问都必须伴随一个非空保护或 ! 声明。我认为这将变得非常烦人。


个人评论: 我认为这是一个合理的决定。问题与数组本质上相关 - 我不认为可以证明(对于编译器)给定位置是否定义,因为您可以使用计算的位置。因此,任何警告都会产生许多错误的结果,并且大多数开发人员都会将其关闭。

基本上,数组提供了比编译器检查更多的自由度。如果要进行适当的检查,则我所能看到的唯一解决方案就是避免直接访问数组,并使用其他提供更多保护的数据结构。


3
非常不幸。 - drkibitz
4
在我看来,这真是一项对语言来说很糟糕的决定。在TypeScript中有许多非常令人恼火的事情 - 但理由充分!我宁愿选择安全且有些恼人,也不愿选择不安全。 - Alex Neth
@AlexNeth:其实,我认为这是一个合理的决定。问题在于数组本身——我认为(对于编译器来说)不可能证明特定位置是否被定义,因为你可以使用计算出的位置。因此,任何警告都会产生很多误报,并且大多数开发人员都会关闭它。如果您想要适当的检查,您将需要使用其他数据结构(例如仅允许追加而不是任意写入的列表)。 - sleske
2
我觉得在用户想要告诉编译器不要尝试的地方,foo?[0]应该是完全可以的。或者至少应该有一个带有严格检查的typescript数组类型。 - Alex Neth
非常好的回答。谢谢。 - Denis Pshenov
TypeScript编译器无法知道某个位置是否已定义,但是ESLint的“no unnecessary condition”规则会在您检查定义性时发出警告。这就是为什么我们不能拥有好东西的原因。 - Agos

8

TypeScript 4.1引入了一个新的编译器选项 - noUncheckedIndexedAccess。此选项会将undefined添加到数组索引访问类型中。

考虑以下代码片段 (TS PlayGround):

const items = [1,2,3]
console.log(items[1]?.toString(10))
console.log(items[2].toString(10))

如果没有启用 noUncheckedIndexedAccessitems[2].toString(10) 将被视为有效;而在启用该选项时则会被视为无效。在旧版答案的 Alex Neth 的评论中也提到,当启用此选项时,items[1]?.toString(10) 将是有效的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接