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


背景

最近接觸了 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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM