nullish 合体演算子 ??

生田 陸人
LuaGate エンジニア / 現役エンジニア
編集 LuaGate編集部

nullish 合体演算子 ?? とは

??nullundefined のときだけデフォルト値を返し、0 や空文字を上書きしない安全な書き方を学ぼう。

nullish 合体演算子 ?? でデフォルト値を返す

JavaScript でとても多いパターンが「値があればそれを使い、無ければデフォルト値を使う」というものです。たとえば API のレスポンスでユーザーの表示名がなければ "ゲスト" を出す、設定値がなければ既定値の 10 を使う、というケース。これを安全に書ける専用演算子が ?? (nullish 合体演算子) です。

??2020 年に追加された比較的新しい演算子で、ES2020 から正式に使えるようになりました。書式は value ?? defaultValue で、「valuenull または undefined のときに限り defaultValue を返す」という挙動です。

?? の合言葉は「nullundefined のときだけ右の値」。それ以外の値、たとえば 0""falseそのまま左の値が採用される ところがミソです。

|| との違いを最初に理解する

デフォルト値の指定には、これまで || (論理 OR) もよく使われてきました。しかし || は「左が falsy なら右」を返すので、0""false のような「正常な値だけど falsy」も上書きしてしまいます。

JavaScript

const countOr = 0 || 10; // 10 になってしまう (0 が falsy) const countNull = 0 ?? 10; // 0 のまま (0 は null ではない) const nameOr = "" || "匿名"; // "匿名" ("" が falsy) const nameNull = "" ?? "匿名"; // "" のまま

0 を正常な値として扱いたい (たとえばカウントや座標) ときに || を使うと、意図せず 10 に書き換えられて困ります。?? を使えば、0"" はそのまま左の値が残り、本当に null / undefined のときだけデフォルトに置き換わります。

文法と動作

基本の書き方は ?? を真ん中に挟むだけです。

JavaScript

function greet(name) { const displayName = name ?? "ゲスト"; return `こんにちは、${displayName} さん`; } console.log(greet("Naoki")); // "こんにちは、Naoki さん" console.log(greet(null)); // "こんにちは、ゲスト さん" console.log(greet(undefined)); // "こんにちは、ゲスト さん" console.log(greet("")); // "こんにちは、 さん" 空文字は残る

namenullundefined のときだけ "ゲスト" が採用されます。空文字 "" を渡したときは "" がそのまま使われるので、文字列の右側がぎこちなくなりますが、それは値の意味としては「明示的に空文字を渡した」ということになります。

Mermaid で ?? の判定を追う

value ?? "default" の流れを図にすると、たった 1 つの分岐です。

diagram (will load when visible)

本当にシンプル。|| だと「falsy か?」というもう少し緩い判定になり、?? は「nullish か?」という厳しい判定になる、というのが両者の違いです。

nullish の正体

JavaScriptnullish と呼ばれるのは nullundefined の 2 つだけです。falsy は次の 6 つです。

  • null
  • undefined
  • 0
  • "" (空文字列)
  • false
  • NaN

?? が反応するのは上の 6 つのうち最初の 2 つだけです。残り 4 つは左の値として尊重されます。この違いを表で覚えておくと、??|| のどちらを選ぶか迷いません。

JavaScript

console.log(null ?? "X"); // "X" console.log(undefined ?? "X"); // "X" console.log(0 ?? "X"); // 0 console.log("" ?? "X"); // "" console.log(false ?? "X"); // false console.log(NaN ?? "X"); // NaN

よくある間違い

?? の落とし穴を 3 つまとめます。

  • ??|| を混同するcount || 10 だと count = 010 に書き換えてしまいます。「0"" を正常な値として残したいとき」は必ず ?? を選ぶこと
  • ??&& || を素のままチェーンするa || b ?? c のように混ぜると構文エラーになります。(a || b) ?? ca || (b ?? c) のように 必ずカッコ で結合順を明示してください
  • 古いブラウザ対応を忘れる?? は ES2020 機能で、IE11 などの古い環境では使えません。Babel などのトランスパイルが必要なケースがあります。最近の Web 開発では実質問題になりません

?? は新しいぶん、コードレビューでも「ここは || のままでいいんじゃない?」と聞かれることがあります。「0 や空文字を残したいから ??」と理由を言えるようになっておきましょう。

代入演算子 ??= も合わせて覚える

おまけで、?? には代入版の ??= もあります。

JavaScript

let name = null; name ??= "ゲスト"; console.log(name); // "ゲスト" let count = 0; count ??= 100; console.log(count); // 0 のまま

x ??= yx = x ?? y と同じ意味です。設定オブジェクトの初期化などで重宝します。

「変数の初期化忘れだけを補いたい」ときに ??= はぴったりです。0 の入った変数は上書きしません。

やってみよう

それでは課題に取り組みましょう。withDefault(value, defaultValue)?? で書いてください。

書き方の方針は次のとおりです。

  • return value ?? defaultValue; の 1 行で完成
  • if (value === null) のような条件分岐を書かないこと (?? の練習)
  • テストには 0""value に渡すケースが含まれます。それらが上書きされないことを確認

?? を使いこなせるようになると、ReactTypeScript の世界でも頻出する「安全なデフォルト値」のパターンが自然に書けるようになります。

よくある質問

Q. ブラウザと Node.js で動作は変わりますか?

A. JavaScript の構文・組み込み機能(配列・文字列・Promise 等)はどちらでも同じです。違いは DOM API(ブラウザ専用)と fs などのファイル系(Node 専用)など、実行環境固有の機能です。コアロジックは両方で動くため、テストもしやすくなります。

Q. ES6 以降の新機能は使っても大丈夫ですか?

A. 現代のブラウザ・Node.js は ES2020 以降の機能をほぼサポートしています。古い IE 等を対象にする必要が無ければ、let/const、アロー関数、分割代入、async/await を積極的に使ってください。トランスパイル(Babel/Vite)が必要なケースは年々減っています。

Q. TypeScript に進む価値はありますか?

A. 型情報があるとリファクタやエディタ補完が劇的に効くため、規模が大きくなるなら TypeScript への移行を強くおすすめします。最初は any を許容しつつ徐々に型を付ければ、JS の知識をそのまま活かせます。

次のレッスン

次は オプショナルチェーン ?. で、??nullundefined のときだけデフォルト値を返し、0 や空文字を上書きしない安全な書き方を学ぼう を学びます。

事前確認 — 進む前に次の 3 つができることを確認しましょう。

  1. ?? 演算子 の要点を自分の言葉で説明できる
  2. このレッスンの最小コード (または操作手順) を見ずに書ける
  3. 練習問題やクイズで間違えた箇所を読み直して理解した

理解度チェック (30 秒)

Q. ?? 演算子 とは何か、1 文で説明してください。

この章のポイント

A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。

関連レッスン

要件

  1. 関数名は withDefault、引数は valuedefaultValue の 2 つにすること
  2. 判定には ?? (nullish 合体演算子) を使うこと
  3. ||if 文で代用しないこと

入出力例

test-cases.txt

withDefault(null, "ゲスト")"ゲスト" withDefault(null, 10)10 withDefault(0, 10)0 withDefault("", "X")"" withDefault(false, true)false withDefault("Naoki", "ゲスト")"Naoki"

ヒント

main.js
main.js
学習モード

メモ

nullish 合体演算子 ??

⌘S で保存