TypeScript 非空断言
本文于336天之前发表,文中内容可能已经过时。
创建了一个 “重学TypeScript” 的微信群,想加群的小伙伴,加我微信 “semlinker”,备注 “1” 。阿里、京东、腾讯的大佬都在群里等你哟。
一、非空断言有啥用
介绍非空断言前,先来看个示例:
1 | function sayHello(name: string | undefined) { |
对于以上代码,TypeScript 编译器会提示一下错误信息:
1 | Type 'string | undefined' is not assignable to type 'string'. |
要解决上述问题,我们可以简单加个条件判断:
1 | function sayHello(name: string | undefined) { |
使用这种方案,问题是解决了。但有没有更简单的方式呢?答案是有的,就是使用 TypeScript 2.0 提供的非空断言操作符:
1 | function sayHello(name: string | undefined) { |
二、非空断言操作符简介
在上下文中当类型检查器无法断定类型时,一个新的后缀表达式操作符 !
可以用于断言操作对象是非 null 和非undefined 类型。具体而言,x!
将从 x 值域中排除 null
和 undefined
。
下面我们来介绍一下非空断言操作符的一些使用场景和注意事项。
2.1 忽略 undefined 和 null 类型
1 | function myFunc(maybeString: string | undefined | null) { |
2.2 调用函数时忽略 undefined 类型
1 | type NumGenerator = () => number; |
2.3 使用非空断言操作符的注意事项
因为 !
非空断言操作符会从编译生成的 JavaScript 代码中移除,所以在实际使用的过程中,要特别注意。
下面我们来举两个简单的示例:
示例一
1 | const a: number | undefined = undefined; |
以上 TS 代码会编译生成以下 ES5 代码:
1 | ; |
虽然在 TS 代码中,我们使用了非空断言,使得 const b: number = a!;
语句可以通过 TypeScript 类型检查器的检查。但在生成的 ES5 代码中,!
非空断言操作符被移除了,所以在浏览器中执行以上代码,在控制台会输出 undefined
。
示例二
1 | type NumGenerator = () => number; |
以上 TS 代码会编译生成以下 ES5 代码:
1 | ; |
若在浏览器中运行以上代码,在控制台会输出以下错误信息:
1 | Uncaught TypeError: numGenerator is not a function |
很明显在运行时,undefined 并不是函数对象,所以就不能正常调用。
需要注意的是,非空断言操作符仅在启用
strictNullChecks
标志的时候才生效。当关闭该标志时,编译器不会检查 undefined 类型和 null 类型的赋值。
三、非空断言操作符使用示例
在以下示例中,首先我们使用 TypeScript 类型别名定义了一个 ListNode
类型,用于表示链表节点。该类型包含 data
和 next
两个属性,分别表示当前节点的值和下个节点。之后,我们还定义了以下两个函数:
- addNext(node: ListNode):用于添加下一个节点;
- setNextValue(node: ListNode, value: number):用于设置下一个节点的值。
1 | type ListNode = { data: number; next?: ListNode; }; |
对于以上代码尽管我们知道在调用 addNext
方法后,node.next 属性会被定义,但 TypeScript 在 node.next.data = value
这行代码中并不能推断出这些。这时候我们可以使用非空断言运算符 !
来断言 node.next 并不是 undefined,并且使编译器警告无效:
1 | function setNextValue(node: ListNode, value: number) { |
接着我们继续看一个示例,假设你有一个表示 AJAX 请求过程的 UI 状态。它要么处于初始状态(initial),要么处于挂起状态(pending),要么处于完成状态(complete),要么处于错误状态(error)。只有在完成状态下才有响应,否则为 null。
1 | type AjaxState<T> = { |
虽然我们知道当请求的状态为 complete
时,响应对象不会为 null,但 TypeScript 并无法感知这些,所以我们还需要使用非空断言 ajaxState.response!.length
来忽略空值并使编译器警告无效。对于这种场景,其实有一个更好的解决方案,即使用可辨识联合:
1 | type AjaxState<T> = |
通过引入可辨识联合类型,我们把为 null 和非 null 的响应完美的区分开来,还避免了再次使用非空断言,此外还大大提高了程序的可读性。在 TypeScript 实际项目的开发过程中,除了使用非空断言(!)之外,读者还可以使用 TypeScript 3.7 版本中新引入的可选链运算符(?.)和空值合并运算符(??)来提高程序的可读性。
四、参考资源
欢迎小伙伴们订阅全栈修仙之路,及时阅读 TypeScript、Node/Deno、Angular 技术栈最新文章。
