Contents
1️⃣ 基本仕様とポイント
| 項目 | 内容 |
|---|---|
| 意味 | 左辺が null または undefined の時だけ右辺を評価し、その結果を返す |
| 対象外の falsy 値 | 0, '', false, NaN はそのまま返る(fallback されない) |
| 評価順序 | 左から右へ短絡的に評価。左辺が nullish でなければ右辺は評価されない |
|
1 2 3 4 5 6 |
null ?? 'default' // → 'default' undefined ?? 42 // → 42 0 ?? 100 // → 0 (fallback されない) '' ?? 'empty' // → '' (空文字もそのまま) NaN ?? 'fallback' // → NaN (NaN は nullish ではない) |
ポイント
??は「null 合体対象」だけを判定するため、意図しない falsy 値の除外が起こりません。デフォルト値を設定したいケースで最も安全です。
2️⃣ || と比較して何が違うか
| 演算子 | 判定対象 | 代表的な落とし穴 |
|---|---|---|
|| |
すべての falsy (0, '', false, NaN, null, undefined) |
0 が 10 に置き換わるなど、意図せぬフォールバックが起こりやすい |
?? |
nullish のみ (null, undefined) |
0 や空文字はそのまま保持できる |
|
1 2 3 4 5 6 7 8 |
// || の例(予期しない置き換え) const count1 = 0 || 10; // → 10 const name1 = '' || 'Guest'; // → 'Guest' // ?? の例(期待通りに動作) const count2 = 0 ?? 10; // → 0 const name2 = '' ?? 'Guest'; // → '' |
3️⃣ 実務でのデフォルト値設定パターン
3.1 シンプルなフォールバック
|
1 2 3 4 |
function getTitle(title) { return title ?? 'Untitled'; } |
API が title を返さない(=null/undefined)場合だけ 'Untitled' に置き換わります。
3.2 フォーム入力の数値変換
|
1 2 3 4 5 6 7 |
// 誤り:NaN は nullish ではないので fallback されない const quantityWrong = Number(input.value) ?? 1; // 正しい例 const raw = input.value.trim(); const quantity = raw === '' ? 1 : Number(raw); |
解説
Number('') は 0 ではなく NaN を返します。NaN ?? 1 は NaN がそのまま結果になるため、期待したデフォルトが適用されません。空文字かどうかを先に判定するか、isNaN() を併用してください。
3.3 オブジェクトの分割代入と組み合わせ
|
1 2 3 4 5 6 7 8 9 |
function createUser({ name, age } = {}) { const userName = name ?? 'Anonymous'; const userAge = age ?? 18; return { name: userName, age: userAge }; } // 呼び出し例 const u1 = createUser({ name: '', age: 0 }); // → { name: '', age: 0 } |
null/undefined のみがデフォルトに置き換わり、空文字や 0 は保持されます。
4️⃣ Optional Chaining (?.) と併用したベストプラクティス
|
1 2 |
const userName = user?.profile?.name ?? '名無し'; |
| シナリオ | 結果 |
|---|---|
user が未定義 |
'名無し' |
profile が未定義 |
'名無し' |
name が空文字 ('') |
''(そのまま) |
name が null / undefined |
'名無し' |
推奨パターン一覧
| 用途 | 書き方 |
|---|---|
| プロパティ取得+デフォルト | obj?.prop ?? defaultVal |
| 配列要素の安全取得 | arr?.[idx] ?? fallback |
| 関数呼び出しのオプショナル化 | service?.fetch()?.data ?? [] |
5️⃣ TypeScript とブラウザ対応
5.1 型推論
|
1 2 3 |
const maybeStr: string | undefined = getValue(); const result = maybeStr ?? 'default'; // result の型は string |
?? の結果は「左辺の型」から null/undefined が除外されたユニオンになります。これにより、後続処理で null チェックを書かずに済みます。
5.2 ブラウザ対応(2024 年時点)
| ブラウザ | バージョン (Native 対応) |
|---|---|
| Chrome | 84+ |
| Edge | 84+ |
| Firefox | 79+ |
| Safari | 14.1+ |
| Opera | 70+ |
注意
上表は執筆時点の情報です。将来的に新しいバージョンがリリースされたら、Can I use – Nullish Coalescing で最新状況を確認してください。
5.3 古い環境へのトランスパイル例(Babel)
|
1 2 3 4 5 |
{ "presets": [["@babel/preset-env", { "targets": "> 0.25%, not dead" }]], "plugins": ["@babel/plugin-proposal-nullish-coalescing-operator"] } |
preset-env が対象ブラウザに合わせてポリフィルを自動挿入します。
6️⃣ よくある誤用と回避策
| 誤用ケース | 問題点 | 回避策 |
|---|---|---|
Number(input.value) ?? 1 |
NaN は fallback されない |
空文字かどうかを先に判定、または isNaN() を使う |
obj ?? { a: 1 }(オブジェクト全体) |
左辺がオブジェクトでも falsy 判定にならずデフォルトが無視される | プロパティ単位で obj.a ?? defaultA と書く |
x ??= y の乱用 |
副作用や意図しない代入が起きやすい | 必要な初期化シーンに限定し、コメントで目的を明示する |
null と undefined を同一視しすぎる |
仕様上は同等だが、API がどちらを返すかで挙動が変わることもある | 受け取るデータの型定義を正確に行い、テストで両方を確認する |
7️⃣ まとめと次のアクション
??はnull/undefinedのみを判定 → falsy 値はそのまま保持できる。||と使い分け → デフォルト値ロジックには必ず??、論理演算が必要なときだけ||を選ぶ。- 数値入力の変換では注意 →
Number('')はNaNになる点を忘れない。空文字判定を先に行うか、isNaN()を併用する。 - Optional Chaining と組み合わせる → 深いプロパティへの安全アクセスとデフォルト値設定が一行で完結。
- TypeScript の型推論を活用 →
null/undefinedが除外された型が得られ、コードの安全性が向上する。 - ブラウザ対応は常に最新情報をチェック → Can I use でサポート状況を確認し、古い環境は Babel 等でトランスパイルする。
実践課題
- 自分のプロジェクト内で||を使っている箇所を検索し、??に置き換えてテストを走らせる。
- フォームや API のサニタイズロジックに??と?.を組み合わせたコードを書いてみる。
これらのステップを踏めば、デフォルト値処理がシンプルかつ安全になり、バグの温床となりがちな falsy 判定から解放されます。ぜひ Nullish Coalescing を活用して、コードベースの可読性と保守性を高めてください。