非空断言(Non-Null Assertion)

非空断言(Non-Null Assertion)

非空断言操作符 ! 是 TypeScript 独有的一项特性,不属于 ECMAScript 标准。非空断言 (Non-Null Assertion) 表示开发者知道这个变量在这里不可能是 null,但是 TypeScript 编译器不知道,这时候就需要我们手动使用 ! 告诉编译器。

在 TypeScript 中,非空断言 (Non-Null Assertion) 的使用场景主要有以下三种:

  1. 用在属性声明或变量声明时
1
2
3
4
5
6
7
8
@ccclass('PlayerController')
export class PlayerController extends Component {
@property({ type: Animation })
public BodyAnim!: Animation

@property({ type: SkeletalAnimation })
public CocosAnim!: SkeletalAnimation
...

比如这里,BodyAnim 和 CocosAnim 我们都是在 Cocos Creator 编辑器中手动完成资源绑定的。这时候就可以确定 BodyAnim 和 CocosAnim 不可能为空,所以可以在这里使用非空断言操作符 !,如果不使用则需要这样:

1
2
3
4
5
@ccclass('PlayerController')
export class PlayerController extends Component {
@property({ type: Animation })
public BodyAnim: Animation | null = null
...

并且在下面使用 this.BodyAnim 时,由于 this.BodyAnim 的类型是 Animation | null 即可能为空,所以需要多一层 if 条件语句判断:

1
2
3
if (this.BodyAnim) {
this.BodyAnim.play('getOneStep')
}

或者使用可选链

1
this.BodyAnim?.play('getOneStep')

但是下面这种情况就不能使用可选链,因为可选链不能出现在赋值表达式左侧

1
2
3
if (this.BodyAnim) {
this.BodyAnim.property = 'value'
}

不管怎样,这些都无疑使得代码变得更加复杂

  1. 在使用变量时

我们可以把上面的 if 条件语句判断改成以下形式:

1
this.BodyAnim!.play('getOneStep')

这段代码与 if 条件语句等价

  1. 在赋值变量前
1
2
3
4
5
6
7
8
const main = () => {
const game = window['game'] = new Game({
width: 1600,
height: 900,
scene: MainScene,
parent: document.getElementById('app')!,
})
}

这里 document.getElementById('app') 的类型是 HTMLDocument | null,可能为 null,所以我们可以通过非空断言 ! 来手动告诉编译器这里这个变量不会为 null。

总结

非空断言操作符 ! 的使用场景是:当你 (开发者) 明确知道这个变量在这个位置不可能为 null 时,可以使用该操作符来强制将这个变量的类型由 SomeType | null 转为 SomeType (即去掉了 null)。使用非空断言操作符可以减少很多无意义的条件语句判断,使得整个代码变得更加简洁。

参考链接

  1. 3.0 TypeScript问题答疑及经验分享 - Cocos 论坛
  2. Non-null assertion operator - TypeScript Documentation