寫 GraphQL 不可不知道的 ECMAScript 的新語法提案 optional chaining 跟 nullish coalescing - Jason's Web Memo

寫 GraphQL 不可不知道的 ECMAScript 的新語法提案 optional chaining 跟 nullish coalescing

說到 JavaScript 最常出現的錯誤,Reference Error: XXX is not defined,Cannot read property XXX of undefined,絕對在排行榜上佔很前面的位置。這樣的錯誤通常發生 api 出問題在 object 因為各種 edge case 的 error 造成缺少欄位,而程式並沒有做好相對應的檢查而直接對其做操作。

自從 destructor 跟 default value 在 ECMAScript 出現以後,我們可以用 default value 來做 defensive programing 去躲掉的問題,用 React 來舉例如下

const Item = ({ title = "defaultTitle", info = {}, coupons = [] }) => {
const { price } = info;
return (
<div>
<div>{title}</div>
<div>{price}</div>
{coupons.map((coupon, i) => (
<span key={i}>{coupon}</span>
))}
</div>
);
};

即使 title info coupons 是 undefined,頂多用 default value 取代,不會造成取值的 error

但是在引進 GraphQL 之後 GraphQL 有一個特性,如果某個欄位 resolve 不出來的時候會在這個欄位回傳 null 不是 undefined,

const query = gql`
{
product {
title
info {
price
}
coupons
}
}
`
;
/*
{
product: {
title: 'title',
info: null,
coupons : null
}
}
*/

而 default value 能用來 fallback undefined 沒有辦法處理 null,可能就會造成意外的 crash。

Component 可能就要改寫如下

const Item = ({ title, info, coupons }) => {
const { price } = info || {};
return (
<div>
<div>{title || "defaultTitle"}</div>
<div>{price}</div>
{coupons && coupons.map((coupon, i) => <span key={i}>{coupon}</span>)}
</div>
);
};

如果遇到更深的 object 你可能會寫出這樣的程式碼

const value =
obj &&
obj.deep &&
obj.deep.deep &&
obj.deep.deep.deep &&
obj.deep.deep.deep.value;

用 && operator 取值還有其他的問題,JavaScript 裡頭 a && b 等價於 a ? b : a

a 可能是 0 空字串 等各種 falsy value 的可能,會造成各種意外的錯誤,譬如下面的例子,你可能就會在畫面上看到一個詭異的 0

const Products = ({ products }) => {
return (
<div>
{products.length &&
products.map((p, i) => <Product key={i} product={p} />)}
</div>
);
};

好吧我們還有 lodash.get

_.get(object, "deep.deep.depp.value");

但是 lodash 會增加不少 js 的 bundle size,你可能會花一番功夫去處理這個問題

ECMAScript 新語法 Optional Chaining

https://github.com/tc39/proposal-optional-chaining

optional chaining 是從其他語言如 swift 借鏡過來處理取值語法,用下圖說明,?. 前面如果是 null undefined 的話就會直接 return undefined,同樣也可以用在 function 上

a?.b; // undefined if `a` is null/undefined, `a.b` otherwise.
a == null ? undefined : a.b;

a?.[x]; // undefined if `a` is null/undefined, `a[x]` otherwise.
a == null ? undefined : a[x];

a?.b(); // undefined if `a` is null/undefined
a == null ? undefined : a.b(); // throws a TypeError if `a.b` is not a function
// otherwise, evaluates to `a.b()`

a?.(); // undefined if `a` is null/undefined
a == null ? undefined : a(); // throws a TypeError if `a` is neither null/undefined, nor a function
// invokes the function `a` otherwise

讓你可以不必要再寫 object field checking code,就可以避免了對 undefined 跟 null 取值的錯誤

ECMAScript 新語法 nullish coalescing

https://github.com/tc39/proposal-nullish-coalescing

|| operator 也是常用來處理 default value 的方法,但是我們要 fallback 成 default value 會讓任何 falsy 的值也都一起 fallback 進去包括 false 空字串,我們可能只是想要 undefined 或是 null 進行 fallback 而已

nullish coalescing 的語法很簡單就只有兩個問號  ?? ,可以來處理只要 fallback null 跟 undefined 的情境

const response = {
settings: {
nullValue: null,
height: 400,
animationDuration: 0,
headerText: "",
showSplashScreen: false,
},
};

const undefinedValue = response.settings.undefinedValue ?? "some other default"; // result: 'some other default'
const nullValue = response.settings.nullValue ?? "some other default"; // result: 'some other default'
const headerText = response.settings.headerText ?? "Hello, world!"; // result: ''
const animationDuration = response.settings.animationDuration ?? 300; // result: 0
const showSplashScreen = response.settings.showSplashScreen ?? true; // result: false

結論

目前 optional chaining 跟 nullish coalescing 已經進到 tc39 的 stage 3 release candidate,babel 也有 implement plugin 應該可以放心使用了,美中不足的缺點是 Typescript 現在尚未支援 (補充 Typescript 3.7 版本就開始支援這兩個語法),可能會破壞 VSCode 的 syntax highlight,然後 babel 其實也是幫你轉成 deep checking 的語法,如果沒有用中間變數暫存去對相同欄位重複使用 optional chaining 可能會造成程式碼些微的體積增加,如果引進 graphql 時為 null 的處理感到頭痛,不妨可以嚐試一下這兩個語法

Webmention 社群迴響 0

喜歡 0
    轉推 0
      引用或評論 0

        用 Webmentions 參與社群迴響

        如果你的 blog 文章想要引用本文,歡迎透過下方表單用將你的 blog 文章網址傳送給我,若你的 blog 文章含有正確的本文網址連結,並且 blog 文章本身支持 microformat,之後你的引用資訊會更新在上面社群迴響的引用評論列表。

        社群迴響將不定期更新,不保證同步,同時有資料缺漏的可能性。

        Jason Chen - Yahoo Taiwan EC Web frontend engineer currently. Write something about web and React.js here

        Jason Chen

        Yahoo Taiwan Sr. Frontend Engineer. Write something about web and React.js here.

        Other Posts