聊聊新特性操作符 ! / ? / ??

背景

最近接触了 ts + react 的项目,项目代码中看到有这样的写法

function demo() {
	return obj.a!; // what's the meaning of 「!」
}

const demoTwo = obj?.a.aChild; // what's the meaning of 「?」

const demoThird = obj ?? anotherObj; // what's the meaning of 「??」

相信前端的同学都能明白 「?」 「??」的意思。如果没接触过 ts,那么对 「!」 大概、也许是陌生的(没错,我说的前端同学就是我自己)

本来以为 [ ? | ?? ]是 es6 的语法,想多了,是 es11(es 2020)了都 😂

看到了,所以想自己记录下,省的每次记不起来又再重复搜索,搜了忘,忘了搜的。好记性不如烂笔头么,记录一下我的学习瞬间。🐶

「?」

名字:可选链/ optionalChaining

可选链操作符( ?. )允许读取位于连接对象链深处的属性的值,而不必明确验证链中的每个引用是否有效。?. 操作符的功能类似于 . 链式操作符,不同之处在于,在引用为空(nullish ) (null 或者 undefined) 的情况下不会引起错误,该表达式短路返回值是 undefined。与函数调用一起使用时,如果给定的函数不存在,则返回 undefined

—— from MDN 可选链

Q:这个操作符解决什么场景的问题呢?

A:有的时候需要对一个对象里边很深的属性取值,但是中间取值的过程可能有的属性为 undefined/null,如果不做处理,就会直接报错了,js 这单线程的玩意儿,一报错后面直接 gg。有了这个操作符,取值的时候不再报错,取不到值会直接返回 undefined。

const demo = obj.a.child.children; 

// 以前如果取这样的值,且为了防止报错,会这样写
const demo = obj && obj.a && obj.a.child && obj.a.child.children;

// 丑、可读性差也就不说了,打这么多字,腱鞘炎又要严重了有木有

// 为了解决这个问题,引入了 ? 操作符

// 新写法
const demo = obj?.a?.child?.children;

// 如果中间某个属性值为 undefined/null,也不会报错。demo 直接为 undefined

「??」

名称:空值合并运算符(Nullish coalescing Operator)

空值合并操作符??)是一个逻辑操作符,当左侧的操作数为 null 或者 undefined 时,返回其右侧操作数,否则返回左侧操作数。

—— from MDN 空值合并运算符

Q:这个操作符解决什么场景的问题呢?不是有 || 了么?

A:逻辑或操作符会在左侧操作数为假值时返回右侧操作数。也就是说,如果使用 || 来为某些变量设置默认值,可能会遇到意料之外的行为。比如为假值(例如,''0)时。(来自MDN)

// 这个例子很好说明了,不再赘述
let user = {
    u1: 0,
    u2: false,
    u3: null,
    u4: undefined
    u5: '',
}
let u1 = user.u1 ?? '用户1'  // 0
let u2 = user.u2 ?? '用户2'  // false
let u3 = user.u3 ?? '用户3'  // 用户3
let u4 = user.u4 ?? '用户4'  // 用户4
let u5 = user.u5 ?? '用户5'  // ''

「!」

名称:非空断言操作符(Non-Null Assertion Operator)

A new ! post-fix expression operator may be used to assert that its operand is non-null and non-undefined in contexts where the type checker is unable to conclude that fact. Specifically, the operation x! produces a value of the type of x with null and undefined excluded. Similar to type assertions of the forms <T>x and x as T, the ! non-null assertion operator is simply removed in the emitted JavaScript code.

——from TS 官网 非空断言

在上下文中当类型检查器无法断定类型时,一个新的后缀表达式操作符 ! 可以用于断言操作对象是非 null 和非 undefined 类型。具体而言,x! 将从 x 值域中排除 null 和 undefined 。! 等同于 <T>x and x as T

—— from 细数 TS 中那些奇怪的符号

特别指出,ts 语言编译成 js 后,非空断言操作符会被移除。

🌟 所以如果要用这个操作符,然后编码还会编译成js的话,注意异常的情况。

我不想写了,引用了 细数 TS 中那些奇怪的符号 的内容

忽略 undefined 和 null 类型
function myFunc(maybeString: string | undefined | null) {
// Type 'string | null | undefined' is not assignable to type 'string'.
// Type 'undefined' is not assignable to type 'string'. 
const onlyString: string = maybeString; // Error
const ignoreUndefinedAndNull: string = maybeString!; // Ok
}

调用函数时忽略 undefined 类型
type NumGenerator = () => number;

function myFunc(numGenerator: NumGenerator | undefined) {
// Object is possibly 'undefined'.(2532)
// Cannot invoke an object which is possibly 'undefined'.(2722)
const num1 = numGenerator(); // Error
const num2 = numGenerator!(); //OK
}

因为 ! 非空断言操作符会从编译生成的 JavaScript 代码中移除,所以在实际使用的过程中,要特别注意。比如下面这个例子:

const a: number | undefined = undefined;
const b: number = a!;
console.log(b); 

以上 TS 代码会编译生成以下 ES5 代码:

"use strict";
const a = undefined;
const b = a;
console.log(b);

虽然在 TS 代码中,我们使用了非空断言,使得 const b: number = a!; 语句可以通过 TypeScript 类型检查器的检查。但在生成的 ES5 代码中,! 非空断言操作符被移除了,所以在浏览器中执行以上代码,在控制台会输出 undefined

附录[3] 中有提到 「可选链」和「非空断言」的区别,我觉得例子写的挺直观的,也贴在这儿了。

const arr = [{value: 1}, {value: 2}];
const item = arr.find(el => el.value === 2);
const result = item.value + 1; //TS error, since item is (allegedly) possibly undefined

如果用可选链 item?.value 时,也挡不住 表达式 undefined +1 的错误。在这个场景下使用 非空断言,排除 null/undefined,可以避免一部分错误。

附录[4]

Non-null assertion operator will not *null guard* your property chain. It just tells TypeScript that the value is never going to be null. But, if your value is indeed null, then your code will crash.

总结

! 是给类型检查用的,于代码逻辑中不起任何作用

? 是真的会影响代码逻辑的

附录

[1] 细数 TS 中那些奇怪的符号

[2] TypeScript 中的代码清道夫:非空断言操作符-中文版

[3] TypeScript 中的代码清道夫:非空断言操作符-英文版

[4] Optional Chaining vs Assertion Operator in TypeScript

posted @ 2022-03-15 23:25  xyJen  阅读(996)  评论(0编辑  收藏  举报