在查看 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 fu 让我失望了。
TS 的感叹号运算符是什么,它是如何工作的?
!
将绕过此错误消息,类似于:“我告诉你,它会在那里。”
那是非空断言运算符。这是一种告诉编译器“这里的表达式不能是 null
或 undefined
,所以不要抱怨它可能是 null
或 undefined
”的一种方式。有时类型检查器本身无法做出决定。
解释here:
一个新的!后缀表达式运算符可用于在类型检查器无法得出该事实的上下文中断言其操作数为非空且非未定义。具体来说,操作 x!产生一个 x 类型的值,其中排除了 null 和 undefined。类似于
我发现在该解释中使用“断言”一词有点误导。从开发人员断言它的意义上说,它是“断言”,而不是要执行测试的意义上。最后一行确实表明它不会发出任何 JavaScript 代码。
路易斯的回答很好,但我想我会尽量简明扼要地总结一下:
bang 运算符告诉编译器暂时放宽它可能要求的“非空”约束。它对编译器说:“作为开发人员,我比你更清楚这个变量现在不能为空”。
var
或 let
声明的变量都被隐式初始化为 undefined
。此外,类实例属性可以这样声明,因此 class C { constructor() { this.myVar = undefined; } }
是完全合法的。最后,生命周期钩子依赖于框架;例如 Angular 和 React 以不同的方式实现它们。所以不能指望 TS 编译器对它们进行推理。
?
说:这可能是 null
,但事实并非如此(因此,如果您绝对知道它不是 null
,则只能使用 !
。
?.
返回类型是可以为空的(即使它永远不会发生),并且您将不得不处理一个可以为空的类型。如果您知道 null 是不可能的,!.
会一劳永逸地修复类型。
非空断言运算符
使用非空断言运算符,我们可以明确地告诉编译器一个表达式的值不是 null
或 undefined
。当编译器无法确定地推断类型但我们比编译器拥有更多信息时,这可能很有用。
例子
TS代码
function simpleExample(nullableArg: number | undefined | null) {
const normal: number = nullableArg;
// Compile err:
// Type 'number | null | undefined' is not assignable to type 'number'.
// Type 'undefined' is not assignable to type 'number'.(2322)
const operatorApplied: number = nullableArg!;
// compiles fine because we tell compiler that null | undefined are excluded
}
编译好的JS代码
请注意,JS 不知道非空断言运算符的概念,因为这是 TS 功能
"use strict";
function simpleExample(nullableArg) {
const normal = nullableArg;
const operatorApplied = nullableArg;
}
简答
非空断言运算符 (!) 帮助编译器确定此变量不是空变量或未定义变量。
let obj: { field: SampleType } | null | undefined;
... // some code
// the type of sampleVar is SampleType
let sampleVar = obj!.field; // we tell compiler we are sure obj is not null & not undefined so the type of sampleVar is SampleType
我的理解是 !
运算符与 NonNullable
做同样的事情。
let ns: string | null = ''
// ^? let ns: string | null
let s1 = ns!
// ^? let s1: string
let s2 = ns as NonNullable<typeof ns>
// ^? let s2: string
不定期副业成功案例分享
!
之前对其执行console.assert()
是一种很好的做法。因为 add!
告诉编译器忽略空检查,所以它在 javascript 中编译为 noop。因此,如果您不确定变量是否为非空,那么最好进行显式断言检查。dict.has(key) ? dict.get(key) : 'default';
之类的代码一起使用,TS 编译器无法推断get
调用永远不会返回 null/undefined。dict.has(key) ? dict.get(key)! : 'default';
正确缩小类型。?.
AFAIK 来自 C# 领域。对于可空类型,C# got its bang too(当然是双关语)。是的,托尼爵士的发明对程序编程世界造成了严重破坏,我们仍在清理余波。作为最可爱的人,他仍然为此道歉。奇怪的是,他对 CS 的主要贡献是关于程序正确性的自动推理(例如,Hoare 逻辑),应用于静态代码分析:他发明了 null 和静态捕获它的方法! :)