在打字稿中,是什么!(惊叹号/ bang)运算符取消引用成员时?


451

在查看tslint规则的源代码时,我遇到了以下语句:

if (node.parent!.kind === ts.SyntaxKind.ObjectLiteralExpression) {
    return;
}

之后请注意!运算符node.parent。有趣!

我首先尝试使用当前安装的TS(1.5.3)版本在本地编译文件。产生的错误指出了爆炸的确切位置:

$ tsc --noImplicitAny memberAccessRule.ts 
noPublicModifierRule.ts(57,24): error TS1005: ')' expected.

接下来,我升级到了最新的TS(2.1.6),可以毫无问题地对其进行编译。因此,这似乎是TS 2.x的功能。但是编译完全忽略了爆炸,导致出现以下JS:

if (node.parent.kind === ts.SyntaxKind.ObjectLiteralExpression) {
    return;
}

到目前为止,我的Google赋使我失败了。

TS的感叹号运算符是什么,它如何工作?

Answers:


686

那就是非null断言运算符。这是一种告诉编译器“此表达式不能为nullor的方式undefined,因此不要抱怨它为nullor 的可能性undefined”。有时类型检查器本身无法确定。

在这里解释:

!在类型检查器无法得出结论的情况下,可以使用新的后缀表达式运算符来断言其操作数是非null且未定义的。具体来说,该运算x!产生xwith nullundefinedexclude 类型的值。类似于表单<T>x和的类型断言x as T!非空断言运算符仅在发出的JavaScript代码中删除。

在该说明中,我发现使用“断言”一词有点误导。在开发人员声明它的意义上,这是“断言” ,而不是在将要执行测试的意义上。最后一行确实表明它没有发出JavaScript代码。


102
很好地呼吁“断言”歧义。
Estus Flask,

8
很好的解释。我发现console.assert()在有问题的变量之前加上a !之后是一个好习惯。由于add !告诉编译器忽略null检查,因此它将编译为javascript中的noop。因此,如果不确定该变量是否为非null,则最好进行显式的断言检查。
Jayesh

12
举一个有启发性的示例:将新的ES Map类型与dict.has(key) ? dict.get(key) : 'default';TS编译器之类的代码一起使用无法推断get调用永远不会返回null / undefined。dict.has(key) ? dict.get(key)! : 'default';正确缩小类型。
kitsu.eb

1
此运算符是否有语,例如猫王运算符如何引用二进制运算符?
ebakunin

@Jayesh您可以在console.assert()优良作法上进行扩展,可以发表示例吗?
Christopher Francisco

167

路易的回答很好,但我想我会总结一下:

bang运算符告诉编译器暂时放宽它可能要求的“非空”约束。它对编译器说:“作为开发人员,我比您更清楚,此变量现在不能为null”。


85
然后,作为开发人员,您搞砸了。
Mike Chamberlain

8
或者,作为编译器,它搞砸了。如果构造函数未初始化属性,但生命周期挂钩执行了该操作,则编译器无法识别该属性。
Mukus

26
这不是TS编译器的责任。与某些其他语言(例如C#)不同,JS(因此是TS)不要求在使用变量之前对其进行初始化。或者,换一种方式来看,在JS中,所有用var或声明的变量let都隐式初始化为undefined。此外,可以这样声明类实例属性,因此class C { constructor() { this.myVar = undefined; } }完全合法。最后,生命周期挂钩是依赖于框架的。例如Angular和React实现它们的方式有所不同。因此,不能期望TS编译器对此进行推理。
Mike Chamberlain '18年

1
考虑到TS中基于控制流的类型分析的强大功能,bang运算符是否存在有效的用例?
尤金·卡拉塔耶夫

@EugeneKarataev是的,框架经常在自己内部初始化变量,而ts控件流分析无法捕获它。它的使用率肯定降低了,但是您会遇到需要它的实例。
arg20
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.