概要
TypeScript 3.7で追加されたNullish Coalescingは現場で使用しておりとても重宝しています。
nullとunderstandのみ判定できるので簡単に任意の型にすることが出来るのですが、
今回、愚直に使用した結果、発生した現象がありました。
今回は例文とそれをトランスパイル後の実際に実行されたコードも出して解説?したいと思います。
もし、今後同じようなことは起きる人がいた時にこの記事で解決の糸口になれば良いなとって思います。
事象
先ずは実際に意図しない時のコードと同等のコードは
interface MyFavorite {
lastName?: string;
firstName?: string;
}
const myFavorite: MyFavorite = {};
const result = myFavorite.lastName && myFavorite.firstName
? myFavorite.lastName + " " + myFavorite.firstName
: myFavorite.lastName ?? "" + myFavorite.firstName ?? ""
;
console.log(result )
とします。
この場合、trueの場合は説明するまでもないと思います。
問題なのはfalse判定だった時です。
この場合、実装者(今回は僕)の期待値としては空文字列を期待していました、
しかし実際に出力された文字は"undefined"と表示されてしました。
もうダメだ。。。
救いはないんですか!?
ってなりました。
原因
この現象を見たとき、処理の評価順がどーなっているかを調べました。
TypeScriptをトランスパイルしてJavaScriptに変換してみました。
そしたら以下コードになりました。
var _a, _b;
const myFavorite = {};
const result = myFavorite.lastName && myFavorite.firstName
? myFavorite.lastName + " " + myFavorite.firstName
: (_b = (_a = myFavorite.lastName, (_a !== null && _a !== void 0 ? _a : "" + myFavorite.firstName)), (_b !== null && _b !== void 0 ? _b : ""));
console.log(result);
となりました。
問題のコードは三項演算子のfalseの部分ですね。
(
_b = (
_a = myFavorite.lastName, (_a !== null && _a !== void 0 ? _a : "" + myFavorite.firstName)
),
(_b !== null && _b !== void 0 ? _b : "")
)
これをみた瞬間全てを理解しました。
処理の順番としては
1. _aにmyFavorite.lastNameを代入(_aにはundefined)
2. _aがnull又はunderstandではない場合、_aはそのまま(今回はこっちではない)
3. _aがnull又はunderstandだった場合、空文字列とmyFavorite.firstNameを結合して_aに代入 ← ここ
4. 以下はそのまま(決してめんどくさいわけではない)
つまり僕の脳内では
(myFavorite.lastName ?? "") + (myFavorite.firstName ?? "")
このようなイメージでいたのですが実際は
((myFavorite.lastName ?? "") + myFavorite.firstName) ?? ""
このように評価されていたそうです。
間違えていたらこっそり教えてください
これでは空文字列とunderstandを結合していることになる為、
"understand"の文字列を生成しておりました。
確かに遅延評価なので理解はしましたが、納得はちょっと・・・てなりました。
まぁ~サボって愚直な実装した僕が悪いんですけどね・・・
最後に
ちょっと使いなれたからって愚直な実装は良くないですね。
このおかげで学ぶ機会があったので良かったですが、改めて慣れって怖いですね。
皆さんも予期せぬ落とし穴に嵌らないように気を付けましょう!!