寫 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 文章本身支持 microformat,歡迎透過下方表單用將你的 blog 文章網址傳送給我,之後你的引用資訊會更新在上面社群迴響的引用評論列表。

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

        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.


        訂閱 blog 掌握最新文章

        訂閱 RSS 更新,複製 xml 網址

        加入 Google Groups 訂閱電子報

        追蹤我的 Medium,文章異步更新

        Other Posts