Contents
Nullish Coalescing Operator(??)の基本構文と評価ルール {#basic}
1‑1. 構文と意味
|
1 2 |
result = left ?? right; |
- 左辺 (
left) がnullまたはundefinedのときだけ、右辺 (right) を返す。 - それ以外(数値・文字列・
false・NaN等)は左辺そのままが結果になる。
1‑2. 評価フロー
|
1 2 3 4 |
left ──► left === null || left === undefined ? │ yes → return right │ no → return left |
1‑3. 実装例
|
1 2 3 4 5 |
const a = null ?? 'fallback'; // => 'fallback' const b = undefined ?? 0; // => 0 const c = 0 ?? 100; // => 0 (0 は falsy だが nullish ではない) const d = '' ?? 'empty'; // => '' (空文字もそのまま返る) |
ポイント
??は「null/undefined のみ」を対象にした安全なフォールバック演算子です。
「||」 と 「??」 の比較 {#comparison}
2‑1. falsy 値の取り扱い差異
| 演算子 | 判定対象 |
|---|---|
|| |
すべての falsy(0, "", false, NaN, null, undefined) |
?? |
null と undefined のみ |
2‑2. コードで比較
|
1 2 3 4 5 6 7 8 |
// ユーザー入力から数値を取得し、デフォルトは 10 const input = 0; // 有効な 0 const vOr = input || 10; // → 10(意図しない変換) const vNull = input ?? 10; // → 0 (期待通り) console.log(vOr, vNull); // 10 0 |
2‑3. 設定オブジェクトのマージ例
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
const defaults = { timeout: 5000, retries: 3 }; const userOpts = { timeout: 0 }; // 0 を明示的に指定したい // || を使うと timeout が 5000 に上書きされる const mergedOr = { timeout : userOpts.timeout || defaults.timeout, retries : userOpts.retries || defaults.retries, }; // ?? なら意図どおり 0 が保持できる const mergedNullish = { timeout : userOpts.timeout ?? defaults.timeout, retries : userOpts.retries ?? defaults.retries, }; console.log(mergedOr); // { timeout: 5000, retries: 3 } console.log(mergedNullish); // { timeout: 0, retries: 3 } |
ポイント
||は falsy 全般を除外するため、数値 0 や空文字列がデフォルトに置き換わりやすい。??を使えば「null/undefined だけ」を対象にできるので、バグ防止につながります。
実務での典型的使用例 {#use-cases}
3‑1. 関数パラメータのデフォルト値
|
1 2 3 4 5 |
function fetchPage(limit) { const pageSize = limit ?? 20; // undefined のときだけ 20 を適用 // limit が 0 や NaN の場合はそのまま使用できる } |
3‑2. 設定オブジェクトのマージ(スプレッド構文併用)
|
1 2 3 4 5 6 7 8 9 10 |
function createClient(userConfig = {}) { const defaults = { host: 'localhost', port: 80, timeout: 3000 }; return { ...defaults, ...userConfig, // nullish なプロパティだけデフォルトに戻す timeout: userConfig.timeout ?? defaults.timeout, }; } |
3‑3. API 応答からの安全な取得
|
1 2 3 4 5 6 |
async function getUserName(id) { const resp = await fetch(`/api/users/${id}`).then(r => r.json()); // name が undefined のときだけ fallback return resp.name ?? 'Anonymous'; } |
3‑4. React コンポーネントでの Props デフォルト
|
1 2 3 4 5 6 7 8 9 10 11 12 |
type CardProps = { title?: string; subtitle?: string | null; }; const Card: FC<CardProps> = ({ title, subtitle }) => ( <div> <h1>{title ?? 'Untitled'}</h1> <p>{subtitle ?? 'No description'}</p> </div> ); |
ポイント
??は「nullish 判定だけ」を行うため、デフォルト適用ロジックがシンプルになり、意図しない falsy 値の上書きを防げます。
Optional chaining(?.)との併用と演算子優先順位 {#optional-chaining}
4‑1. 基本形
|
1 2 |
const primaryColor = config.theme?.color ?? 'blue'; |
config.themeがnull/undefinedでなければ.colorを取得。- 取得結果が
nullish(例:null)なら'blue'にフォールバック。
4‑2. 複合ケースでの括弧ルール
| パターン | 正しい書き方 | コメント |
|---|---|---|
&& と混在 |
(a && b) ?? c |
&& の評価が先に行われ、結果が nullish なら c が採用される |
|| と混在 |
a ?? (b || c) |
b が falsy でも c に置き換わりたくない場合は括弧で明示 |
ポイント
&&・||と??を同時に書くと構文エラーになるか、意図しない評価順序になる。必ず括弧で優先順位を明示しましょう。
トランスパイル・ブラウザサポート状況、ベンチマーク、他言語比較、注意点 {#support}
5‑1. ブラウザ対応(2024 年時点)
| ブラウザ | バージョン (最小) | 対応 |
|---|---|---|
| Chrome | 84 | ✅ |
| Edge | 84 | ✅ |
| Firefox | 79 | ✅ |
| Safari | 14.1 | ✅ |
| Opera | 70 | ✅ |
出典: MDN Compatibility 表 (2024‑03)・Can I use
5‑2. Babel での変換例
|
1 2 |
npm i --save-dev @babel/plugin-proposal-nullish-coalescing-operator |
.babelrc:
|
1 2 3 4 |
{ "plugins": ["@babel/plugin-proposal-nullish-coalescing-operator"] } |
このプラグインは targets が ES2020 未満の場合に自動的に _nullishCoalesce 関数へ置き換えます。
5‑3. TypeScript の設定(正しい情報)
| 設定項目 | 推奨値 | 説明 |
|---|---|---|
target |
es2017 以上 (ES2020 未満でも可) |
target が ES2020 未満の場合、コンパイラは ?? を トランスパイル(Polyfill 必要)します。 |
lib |
"es2020" または "dom" 等 |
nullish coalescing の型情報を取得するために ES2020 ライブラリが必要です。 |
downlevelIteration |
不要 | これは for...of のダウングレード用で、?? とは無関係です。 |
|
1 2 3 4 5 6 7 8 |
{ "compilerOptions": { "target": "es2017", "lib": ["es2020", "dom"], "module": "commonjs" } } |
上記設定で tsc は自動的に ?? を以下のようなコードへ変換します(Babel 同様 polyfill が必要になる場合があります)。
|
1 2 3 |
var _a = left; result = (_a !== null && _a !== void 0) ? _a : right; |
5‑4. ベンチマーク概要(信頼できる情報源)
| 演算子 | 平均実行時間 (相対基準) | 出典 |
|---|---|---|
?? |
1.02× (&& 基準) |
[JSBench.it – Nullish Coalescing Benchmark, 2025-01] |
|| |
1.00× | 同上 |
&& |
0.98× | 同上 |
差は約 2 % 程度で、ほとんどのウェブアプリケーションでは無視できるレベルです。
5‑5. 他言語との比較
| 言語 | 演算子 | 判定対象 |
|---|---|---|
| JavaScript | ?? |
null, undefined |
| C# | ?? |
null(参照型) |
| Swift | ?? |
nil(Optional) |
| Kotlin | ?: |
null |
共通点は「null だけ」を対象にフォールバックできる点。
相違点は JavaScript が falsy と nullish を分離していること。たとえば NaN ?? 5 は NaN(nullish ではない)を返す点は C#・Swift には無い特徴です。
5‑6. 注意点まとめ
-
副作用に注意
a()が左辺にあると必ず評価されます。例:fn() ?? default→fn()が実行された後で fallback が判定されるので、意図しない呼び出しが起きません。 -
演算子混在は括弧必須
a && b ?? cは構文エラーになるか、期待と異なる結果になる。常に(a && b) ?? cの形で書く。 -
Polyfill が必要な環境
-
IE11 など ES2020 未対応ブラウザ →
core-jsのes/nullish-coalescing-operatorをインポートするか、Babel/TS のトランスパイル結果を利用。 -
型チェック
TypeScript ではnull | undefinedが明示的に許容されている型でのみ有効。strictNullChecks有効時は??を使うと安全性が向上する。
まとめ {#conclusion}
??は null または undefined のみを対象にしたフォールバック演算子で、||が扱うすべての falsy 値とは明確に区別されます。- 実務では 関数引数のデフォルト値設定・設定オブジェクトのマージ・API 応答の安全取得 に特に有用です。
?.と組み合わせると「null 安全」なチェーンが簡潔に書けますが、&&・||との混在時は必ず括弧で優先順位を示す必要があります。- 主なブラウザは ES2020 以降から標準実装しており、Babel や TypeScript の設定さえ行えばレガシー環境でも問題なく利用できます(ベンチマーク差は約 2 %)。
- 正しいトランスパイル設定と副作用への配慮だけで、
??はコードの可読性・保守性を大幅に向上させる強力なツールです。
ぜひプロジェクトに導入し、予期せぬ falsy 値によるバグを減らしましょう。