JavaScript/TypeScript

真偽値と条件の落とし穴

8
  • JavaScript

真偽値と条件の落とし穴 — JavaScript Tips

真偽値と条件の落とし穴 — JavaScript Tips

if&& / || は、値を真偽に変換してから分岐します。falsy になるのは false / 0 / "" / null / undefined / NaN など少数です。||???. の差を知っておくと、フォームや設定で 0 や空文字を消してしまう事故を避けやすくなります。

参考: Truthy · Falsy · Nullish coalescing operator (??) · Optional chaining (?.) · 論理演算子


目次

  1. Truthy と Falsy とは
  2. if と Boolean 変換
  3. デフォルト値と論理演算子
  4. 安全なプロパティアクセス
  5. 短絡評価の実用パターン
  6. 比較と真偽値の混同
  7. よくある失敗

Truthy と Falsy とは

条件式では、値は自動的に真偽値に変換されます。

Falsy(偽とみなされる 8 つ)

補足
false 論理偽
0 数値のゼロ
-0 同上
0n BigInt のゼロ
"" 空文字列
null 意図的な「値なし」
undefined 未定義
NaN 非数

これ以外はほぼ Truthy です。例えば [](空配列)、{}(空オブジェクト)、"0"(文字列のゼロ)はすべて truthy です。

if ([]) {  console.log("空配列は truthy"); // 実行される}if ("") {  // 実行されない} else {  console.log("空文字は falsy");}

「値があるかどうか」を判定するとき、空配列や空オブジェクトを「データなし」と誤認 しないよう注意が必要です。


if と Boolean 変換

明示的に真偽値へ変換するには Boolean() や二重否定 !! を使います。

const input = "  ";console.log(Boolean(input)); // true(空白だけの文字列は truthy)console.log(!!input);        // trueconst count = 0;if (count) {  // 実行されない — 0 は falsy}// 数値 0 を有効な値として扱うならif (count !== undefined && count !== null) {  console.log("count は 0 かもしれないが、設定はされている");}

「ユーザーが入力したか」を空文字で判定する例です。

function isFilled(value) {  return value !== "" && value !== null && value !== undefined;}isFilled(0);       // trueisFilled("");    // falseisFilled(null);  // false

デフォルト値と論理演算子

||(論理 OR)

左辺が falsy なら右辺を返します。

const name1 = "" || "ゲスト";console.log(name1); // "ゲスト"const port1 = 0 || 3000;console.log(port1); // 3000 — 0 も falsy なので置き換わる

??(Nullish coalescing)

左辺が null または undefined のときだけ 右辺を返します。

const name2 = "" ?? "ゲスト";console.log(name2); // "" — 空文字は残るconst port2 = 0 ?? 3000;console.log(port2); // 0 — 0 は有効な値として残るconst missing = null ?? "デフォルト";console.log(missing); // "デフォルト"

デフォルト値の設定 では、0"" を正当な値として扱うかどうかで ||?? を選び分けます。

&&(論理 AND)

左辺が falsy なら左辺を、そうでなければ右辺を返します(短絡評価)。

const user = { name: "田中" };const greeting = user && user.name && `こんにちは、${user.name}`;console.log(greeting); // "こんにちは、田中"const empty = null;console.log(empty && empty.name); // null(エラーにならない)

&& は「左が存在するときだけ右を評価する」ガードにも使えます。ネストが深くなるなら ?. の方が追跡しやすいです。


安全なプロパティアクセス

Optional chaining ?. は、参照先が null または undefined のとき エラーにせず undefined を返します

const response = {  user: {    profile: {      city: "東京",    },  },};console.log(response.user?.profile?.city);  // "東京"console.log(response.admin?.profile?.city); // undefined(admin がない・TypeError にならない)// メソッド呼び出しconst obj = { greet() { return "hi"; } };console.log(obj.greet?.());   // "hi"console.log(obj.missing?.()); // undefined// 配列の要素const arr = [1, 2, 3];console.log(arr?.[10]); // undefined

?.参照の連鎖が途切れた時点で止まる ので、深いネストの API レスポンスを扱うときに便利です。

?.?? を続ける書き方も定番です。

const city = response.user?.profile?.city ?? "未設定";console.log(city);

短絡評価の実用パターン

デフォルト値

// 環境変数風のオブジェクトconst config = { timeout: 0 };const timeout = config.timeout ?? 5000;const retries = config.retries ?? 3;

条件付き実行

const isDev = true;isDev && console.log("デバッグモード");// 関数があるときだけ呼ぶconst onSuccess = null;onSuccess?.();

値の選択(|| の古典パターン)

function displayName(user) {  return user.nickname || user.name || "匿名";}

ニックネームが空文字 "" のときも本名にフォールバックしたいなら || が意図に合います。0 を残したいフィールドでは ?? を使います。

三項演算子との使い分け

const status = isActive ? "有効" : "無効";// 複雑な条件は if のほうが読みやすいことが多いlet label;if (score >= 90) {  label = "A";} else if (score >= 70) {  label = "B";} else {  label = "C";}

比較と真偽値の混同

== と ===

== は型変換を伴う比較です。意図しない truthy/falsy との混同を避けるため、通常は === を使う のが無難です。

console.log(0 == false);   // trueconsole.log(0 === false);  // falseconsole.log("" == false);  // true

真偽値との比較

const flag = true;if (flag === true) { /* OK */ }if (flag) { /* これで十分なことが多い */ }// NG になりがちif (flag == true) { /* 動くが冗長 */ }

Boolean オブジェクト

new Boolean(false) はオブジェクトなので truthy です。プリミティブの false と混同しないでください。

const boxed = new Boolean(false);if (boxed) {  console.log("オブジェクトは truthy"); // 実行される}

よくある失敗

空配列を「データなし」と判定していない

const items = [];if (items) {  console.log("一覧を描画"); // 空なのに描画処理に入る}// 件数で判定するif (items.length > 0) {  console.log("一覧を描画");}

?? と || を混ぜてパースエラーになる

??|| / &&括弧なしで混在 させると構文エラーになります。

// NG// const x = a || b ?? c;// OKconst x = (a || b) ?? c;const y = a || (b ?? c);

?. の戻り値が undefined なのにメソッドを呼んでいる

const data = null;// NG: ?. の結果にさらに通常の . でアクセス// data?.user.name; // user が undefined なら .name でエラー// OKdata?.user?.name;

?.その演算の直前 だけを短絡します。チェーン全体に ?. を付けましょう。

0 や "" を消してしまう

const quantity = 0;const display = quantity || "未入力"; // "未入力" になってしまうconst fixed = quantity ?? "未入力";    // 0 のまま

フォームの数値や金額では ?? を優先的に検討します。

&& で代入しようとして値がおかしい

let value;value = someCondition && "設定値";// someCondition が false なら value は false(文字列ではない)

意図が「条件付きで文字列を入れる」なら三項演算子のほうが明確です。

value = someCondition ? "設定値" : undefined;

if&& / || は、値を真偽に変換してから分岐します。falsy になるのは false / 0 / "" / null / undefined / NaN など少数です。||???. の差を知っておくと、フォームや設定で 0 や空文字を消してしまう事故を避けやすくなります。

参考: Truthy · Falsy · Nullish coalescing operator (??) · Optional chaining (?.) · 論理演算子


目次

  1. Truthy と Falsy とは
  2. if と Boolean 変換
  3. デフォルト値と論理演算子
  4. 安全なプロパティアクセス
  5. 短絡評価の実用パターン
  6. 比較と真偽値の混同
  7. よくある失敗

Truthy と Falsy とは

条件式では、値は自動的に真偽値に変換されます。

Falsy(偽とみなされる 8 つ)

補足
false 論理偽
0 数値のゼロ
-0 同上
0n BigInt のゼロ
"" 空文字列
null 意図的な「値なし」
undefined 未定義
NaN 非数

これ以外はほぼ Truthy です。例えば [](空配列)、{}(空オブジェクト)、"0"(文字列のゼロ)はすべて truthy です。

if ([]) {  console.log("空配列は truthy"); // 実行される}if ("") {  // 実行されない} else {  console.log("空文字は falsy");}

「値があるかどうか」を判定するとき、空配列や空オブジェクトを「データなし」と誤認 しないよう注意が必要です。


if と Boolean 変換

明示的に真偽値へ変換するには Boolean() や二重否定 !! を使います。

const input = "  ";console.log(Boolean(input)); // true(空白だけの文字列は truthy)console.log(!!input);        // trueconst count = 0;if (count) {  // 実行されない — 0 は falsy}// 数値 0 を有効な値として扱うならif (count !== undefined && count !== null) {  console.log("count は 0 かもしれないが、設定はされている");}

「ユーザーが入力したか」を空文字で判定する例です。

function isFilled(value) {  return value !== "" && value !== null && value !== undefined;}isFilled(0);       // trueisFilled("");    // falseisFilled(null);  // false

デフォルト値と論理演算子

||(論理 OR)

左辺が falsy なら右辺を返します。

const name1 = "" || "ゲスト";console.log(name1); // "ゲスト"const port1 = 0 || 3000;console.log(port1); // 3000 — 0 も falsy なので置き換わる

??(Nullish coalescing)

左辺が null または undefined のときだけ 右辺を返します。

const name2 = "" ?? "ゲスト";console.log(name2); // "" — 空文字は残るconst port2 = 0 ?? 3000;console.log(port2); // 0 — 0 は有効な値として残るconst missing = null ?? "デフォルト";console.log(missing); // "デフォルト"

デフォルト値の設定 では、0"" を正当な値として扱うかどうかで ||?? を選び分けます。

&&(論理 AND)

左辺が falsy なら左辺を、そうでなければ右辺を返します(短絡評価)。

const user = { name: "田中" };const greeting = user && user.name && `こんにちは、${user.name}`;console.log(greeting); // "こんにちは、田中"const empty = null;console.log(empty && empty.name); // null(エラーにならない)

&& は「左が存在するときだけ右を評価する」ガードにも使えます。ネストが深くなるなら ?. の方が追跡しやすいです。


安全なプロパティアクセス

Optional chaining ?. は、参照先が null または undefined のとき エラーにせず undefined を返します

const response = {  user: {    profile: {      city: "東京",    },  },};console.log(response.user?.profile?.city);  // "東京"console.log(response.admin?.profile?.city); // undefined(admin がない・TypeError にならない)// メソッド呼び出しconst obj = { greet() { return "hi"; } };console.log(obj.greet?.());   // "hi"console.log(obj.missing?.()); // undefined// 配列の要素const arr = [1, 2, 3];console.log(arr?.[10]); // undefined

?.参照の連鎖が途切れた時点で止まる ので、深いネストの API レスポンスを扱うときに便利です。

?.?? を続ける書き方も定番です。

const city = response.user?.profile?.city ?? "未設定";console.log(city);

短絡評価の実用パターン

デフォルト値

// 環境変数風のオブジェクトconst config = { timeout: 0 };const timeout = config.timeout ?? 5000;const retries = config.retries ?? 3;

条件付き実行

const isDev = true;isDev && console.log("デバッグモード");// 関数があるときだけ呼ぶconst onSuccess = null;onSuccess?.();

値の選択(|| の古典パターン)

function displayName(user) {  return user.nickname || user.name || "匿名";}

ニックネームが空文字 "" のときも本名にフォールバックしたいなら || が意図に合います。0 を残したいフィールドでは ?? を使います。

三項演算子との使い分け

const status = isActive ? "有効" : "無効";// 複雑な条件は if のほうが読みやすいことが多いlet label;if (score >= 90) {  label = "A";} else if (score >= 70) {  label = "B";} else {  label = "C";}

比較と真偽値の混同

== と ===

== は型変換を伴う比較です。意図しない truthy/falsy との混同を避けるため、通常は === を使う のが無難です。

console.log(0 == false);   // trueconsole.log(0 === false);  // falseconsole.log("" == false);  // true

真偽値との比較

const flag = true;if (flag === true) { /* OK */ }if (flag) { /* これで十分なことが多い */ }// NG になりがちif (flag == true) { /* 動くが冗長 */ }

Boolean オブジェクト

new Boolean(false) はオブジェクトなので truthy です。プリミティブの false と混同しないでください。

const boxed = new Boolean(false);if (boxed) {  console.log("オブジェクトは truthy"); // 実行される}

よくある失敗

空配列を「データなし」と判定していない

const items = [];if (items) {  console.log("一覧を描画"); // 空なのに描画処理に入る}// 件数で判定するif (items.length > 0) {  console.log("一覧を描画");}

?? と || を混ぜてパースエラーになる

??|| / &&括弧なしで混在 させると構文エラーになります。

// NG// const x = a || b ?? c;// OKconst x = (a || b) ?? c;const y = a || (b ?? c);

?. の戻り値が undefined なのにメソッドを呼んでいる

const data = null;// NG: ?. の結果にさらに通常の . でアクセス// data?.user.name; // user が undefined なら .name でエラー// OKdata?.user?.name;

?.その演算の直前 だけを短絡します。チェーン全体に ?. を付けましょう。

0 や "" を消してしまう

const quantity = 0;const display = quantity || "未入力"; // "未入力" になってしまうconst fixed = quantity ?? "未入力";    // 0 のまま

フォームの数値や金額では ?? を優先的に検討します。

&& で代入しようとして値がおかしい

let value;value = someCondition && "設定値";// someCondition が false なら value は false(文字列ではない)

意図が「条件付きで文字列を入れる」なら三項演算子のほうが明確です。

value = someCondition ? "設定値" : undefined;

シェア