`Cannot read properties of undefined` を止めるために、見つけたプロパティへ片っ端から `?.` を付けていないでしょうか。Optional Chaining(オプショナルチェーン)は、`null` や `undefined`
の可能性がある値へ安全にアクセスするための便利な構文です。ただし、エラーを出さなくすることと、コードが正しくなることは同じではありません。この記事では `?.`
の書き方だけでなく、「欠損を許容してよい場所か」を基準に、使う場所・使わない場所をコードで整理します。
結論:「欠損を許容してよい場所」で使う
Optional Chainingを使うか迷ったら、最初に確認したいのは「その値が無くても正常な状態と言えるか」です。ユーザーの任意プロフィール、存在しないことが仕様上あり得るAPI項目、任意コールバックなどは `?.`
と相性が良い対象です。
| 状況 | おすすめ | 理由 |
|---|---|---|
| 途中の値が無いことを仕様として許容する | `?.` を使う | `null` / `undefined` で安全に短絡し、`undefined` を返せる |
| 本来必須のオブジェクトが無い | 入力・状態を検証する | `?.` で握りつぶすと、本来の不具合に気付きにくい |
| 無い場合の表示値・既定値が必要 | `?.` と `??` を組み合わせる | 欠損時の挙動を明示できる |
| 変数自体が宣言されていない | 宣言・スコープを直す | `undeclaredVar?.prop` でもReferenceErrorになる |
つまり `?.` は「エラーを消す記号」ではなく、「ここは値が無い状態を許容する」という設計意図をコードに表すために使う、と考えると判断しやすくなります。
Optional Chaining(?.)とは
Optional Chaining(`?.`)は、オブジェクトのプロパティ参照や関数呼び出しを行うとき、評価対象が `null` または `undefined` なら、その時点で処理を短絡して `undefined` を返す構文です。ECMAScript
2026仕様でも、Optional Chainの基底値が `undefined` または `null` の場合は `undefined` を返すことが定義されています。
用語解説:nullish
JavaScriptでは、`null` または `undefined` のどちらかである状態をまとめてnullishと呼びます。Optional Chainingが短絡するのは、この2種類です。
const user = {};
// profileがないためTypeError
// console.log(user.profile.name);
const name = user.profile?.name;
console.log(name); // undefined
`user.profile?.name` では、`profile` が `undefined` なので `.name` へ進まず、式全体が `undefined` になります。`false`、`0`、空文字列のような値まで欠損扱いするわけではありません。
(Cannot read properties系のエラーが起きる原因から切り分けたい場合については『TypeError: Cannot read property ‘xxx’ of undefined の原因と対処法を徹底解説【初心者向け】』をご参照ください)
プロパティ・配列・関数での使い方
`?.` は、通常のドット記法だけでなく、配列や動的キーのブラケット記法、任意の関数呼び出しにも使えます。まずは「値が無いことが仕様上あり得る場所」に限定して使うのが安全です。
// 1. プロパティ
const city = user.address?.city;
// 2. 配列・動的キー
const firstItem = response.items?.[0];
const key = "theme";
const theme = settings?.[key];
// 3. 任意のコールバック
onSuccess?.(result);
関数呼び出しでは、対象の関数が存在しなければ `undefined` が返ります。ただし、プロパティ自体は存在するものの値が関数ではない場合、`callback?.()` としても `TypeError`
になります。オブジェクト側も欠損し得るなら `service?.run?.()` のように、それぞれの境界を分けて考えます。
??と組み合わせて既定値を決める
Optional Chainingで得られる値は、途中が欠損していれば `undefined` になります。画面表示などで既定値が必要なら、Nullish Coalescing演算子(`??`)を組み合わせると意図を明確にできます。
const city = user.address?.city ?? "未設定";
const firstName = users?.[0]?.name ?? "該当ユーザーなし";
`??` が右辺へ切り替わるのは `null` または `undefined` のときだけです。`||` は `0`、`false`、空文字列も偽値として右辺へ切り替えるため、0件や空文字列を有効値として残したい場面では挙動が異なります。
使いすぎるとバグを隠す:?.を使わない方がよい場面
一番注意したいのは、本来必須のデータに `?.` を重ねて「エラーだけ消す」使い方です。たとえば注文オブジェクトが必須なのに、`order?.items?.`
と書けば異常な状態でも処理が静かに進みます。結果として、原因の発見が遅れることがあります。
// 悪い例:order自体が必須なのに欠損を許容してしまう
const total = order?.items?.reduce(
(sum, item) => sum + item.price,
0
);
// 改善例:必須値は先に検証する
if (!order) {
throw new Error("order is required");
}
const total = order.items?.reduce(
(sum, item) => sum + item.price,
0
) ?? 0;
- API契約で必須とされているレスポンス項目
- アプリ起動に必要な設定オブジェクト
- 状態管理上、存在しないと設計違反になる値
- 必須フォーム値や必須ルートパラメータ
こうした値は、使用地点で `?.` を追加するより、データを受け取る境界で検証する方が原因を追いやすくなります。Optional
Chainingは「欠損してよい値」のために残しておくと、コードから設計意図も読み取りやすくなります。
(そもそもundefinedがどこで生まれているかをデバッグしたい場合については『なぜ「undefined」が出る?原因と解決法10選|JavaScript のデバッグに効く実例集』をご参照ください)
?.でも防げないケース
`?.` を付ければ、どんなTypeErrorやReferenceErrorも防げるわけではありません。特に次の4つは、実務で「?.を付けたのに落ちる」原因になりやすいポイントです。
| ケース | コード例 | 結果 |
|---|---|---|
| 未宣言の変数 | `undeclaredVar?.prop` | ReferenceError。Optional Chainingは未宣言ルートを作らない |
| 存在するが関数ではない | `obj.method?.()` | `method`が非関数ならTypeError |
| チェーン途中をグルーピング | `(user?.profile).name` | `user`がnullishなら、その後の`.name`でTypeErrorになり得る |
| 代入先として使う | `obj?.prop = 1` | SyntaxError。Optional Chainの評価結果には代入できない |
const user = null;
// 連続したチェーンなら短絡する
const safeName = user?.profile?.name;
// グルーピングすると、その後の .name は別評価になる
// const unsafeName = (user?.profile).name; // TypeError
MDNでは、`new Intl?.DateTimeFormat()` のように `new` のコンストラクター部分へOptional Chainingを使う構文や、タグ付きテンプレートへ使う構文も無効とされています。`?.`
は「プロパティ参照・ブラケットアクセス・関数呼び出しなら何でも置ける記号」ではない点に注意してください。
実務で迷わない判断フロー
コードレビューで `?.` の妥当性を判断するときは、構文から見るより、値の契約から逆算すると迷いにくくなります。
- 1. その値は無くても正常か。YESなら `?.` の候補にする
- 2. 無いこと自体が異常か。YESなら、`?.` ではなく入力・状態を先に検証する
- 3. 欠損時に表示値や既定値が必要か。必要なら `?.` の後ろへ `??` を付ける
- 4. `?.` を追加してエラーが消えた後、なぜnullishになったかを説明できるか確認する
- 5. チェーンが長い場合は、どの段階まで欠損を許容する仕様なのかを明確にする
たとえば `user?.company?.address?.city` が動くことと、`user`・`company`・`address` のすべてが任意でよいことは別問題です。仕様上任意なのが `address` だけなら、上流で `user` と `company`
を保証したうえで `user.company.address?.city` とした方が、異常と許容可能な欠損を区別できます。
まとめ:?.はエラーを消す道具ではなく「欠損を許容する」宣言
- Optional Chainingは、対象が`null`または`undefined`なら短絡して`undefined`を返す
- `obj?.prop`、`obj?.[expr]`、`func?.()`の3パターンを基本として覚える
- 欠損時の既定値が必要なら`??`と組み合わせる
- 必須データに`?.`を重ねると、エラーは消えても不具合の原因を隠す可能性がある
- 未宣言変数、非関数呼び出し、グルーピング、無効な代入などは`?.`でも解決しない
`Cannot read properties of undefined` に遭遇したら、まず「このプロパティは本当に任意なのか」を確認してください。任意なら `?.`、必須なら上流のデータや状態を直す。この分け方を先に決めると、Optional
Chainingを便利に使いながら、エラーの原因まで隠してしまうコードを減らせます。