背景
最近接觸了 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 operationx!
produces a value of the type ofx
withnull
andundefined
excluded. Similar to type assertions of the forms<T>x
andx 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
andx 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 中的代碼清道夫:非空斷言操作符-中文版