TypeScriptで、ネストされたオブジェクトのプロパティにアクセスする際、hoge.fuga.piyoのようなコードを書くことがあります。しかし、hugaがnullやundefinedだった場合、Cannot read properties of null (reading ‘piyo’)という実行時エラーに遭遇します。
この問題をスマートに解決してくれるのが、オプショナルチェーン (?.) です。
今回は、このオプショナルチェーンの使い方と、その裏側で何が起きているのか、そして「本当にそれ、必要?」という話をしたいと思います。
オプショナルチェーン (?.) とは?
オプショナルチェーンは、参照がnullまたはundefinedの可能性がある場合に、プロパティへのアクセスを安全に行うための機能です。
公式により詳しく書いてあります。
.png?pattern=cross&md=0&fontSize=75px&textColor=%23ffffff&textStrongColor=%238340BB&overlay=https%3A%2F%2Fraw.githubusercontent.com%2Fyytypescript%2Fog-image%2Fmain%2Fpublic%2Fogp-overlay.svg)
コンパイル後のJavaScriptはどうなっている?
この便利なオプショナルチェーン(?.)ですが、TypeScriptからJavaScriptにコンパイルされると、どのようなコードに変換されるのでしょうか?
実際にTypeScript Playgroundで確認してみます。
▼ TypeScriptのコード
const a = {b: 1}
const c = a.b
const d = a?.b
▼ コンパイル後のJavaScript (ES5)
"use strict";
const a = { b: 1 };
const c = a.b;
const d = a === null || a === void 0 ? void 0 : a.b;
見ての通り、?.というシンプルな記法が、コンパイル後には a === null || a === void 0 ? void 0 : a.b; という、少し複雑な三項演算子に変換されています。
本当にオプショナルチェーン (optional chaining)は必要?
オプショナルチェイニングは非常に便利ですが、私は「頼りすぎるべきではない」と考えています。
なぜなら、TypeScriptの最大の強みは「型」による静的解析で安全性を担保することだからです。
オプショナルチェーンは、いわば「実行時」のエラーを防ぐためのセーフティネットです。しかし、そもそも「コンパイル時」にnullやundefinedの可能性を排除できるなら、それに越したことはありません。コード量も減り、可読性も上がります。
APIのレスポンスなど、外部要因でどうしてもオプショナルになってしまう場合は活躍します。しかし、アプリケーション内部のロジックであれば、より厳密な型を定義することで、オプショナルチェーンを使わなくても済む、より堅牢なコードを書けることが多いです。