truthy / falsy と && || ??

Part 0 · React の前に

truthy / falsy と && || ??

条件によって出し分けるための土台

React で「件数が 0 のときは何も出さない」と書いたはずなのに、 画面に 0 だけがぽつんと表示される。 これは初心者が必ず一度は遭遇するバグです。

原因は && という記号の性質にあります。これは「かつ」ではなく、値を返す演算子だからです。

この章では、そのからくりを先に潰しておきます。

JavaScript は何でも真偽に変換する

if の中に true / false 以外を書いても、JavaScript は文句を言いません。 勝手に「真っぽいか、偽っぽいか」に変換して判断します。

このとき偽として扱われる値は 6 つだけです。 これ以外は全部、真として扱われます。

偽になる値と、間違えやすい値

空の配列も空のオブジェクトも「真」になる

偽として扱われる(これで全部)

  • false
  • 0
  • ""
  • null
  • undefined
  • NaN

真として扱われる(間違えやすいもの)

  • []
  • {}
  • "0"
  • "false"
  • -1
  • " "

falsy.ts

// 偽として扱われる値は、この 6 つだけ
export const falsyValues = [false, 0, "", null, undefined, NaN];

// それ以外は全部「真」として扱われる。
// 中身が空でも、文字が "0" や "false" でも真になる
export const truthyButSurprising = [[], {}, "0", "false", -1, " "];

&& と || は「値を返す」

ここが本題です。&&|| は、true か false を返すのではありません。 左右どちらかの値そのものを返します。

  • A && B … A が偽なら A を返す。真なら B を返す
  • A || B … A が真なら A を返す。偽なら B を返す
  • A ?? B … A が nullundefined のときだけ B を返す
同じ形でも、返るものが違う

0 のときの結果に注目

  • count && "あり"0
  • stock && "あり""あり"
  • keyword || "デフォルト""デフォルト"
  • count || "デフォルト""デフォルト"
  • count ?? "デフォルト"0
  • nickname ?? "デフォルト""デフォルト"

count が 0 のとき、count && "あり" の結果は false ではなく 0 です。 左が偽だったので、そのまま左の値が返っています。

operators.ts

/*
  && は 左が偽ならその左の値を返し、真なら右の値を返す
  || は 左が真ならその左の値を返し、偽なら右の値を返す
  ?? は 左が null か undefined のときだけ右の値を返す
*/

// 実際のデータと同じように、変数に入った値で試す
const count: number = 0;
const stock: number = 5;
const keyword: string = "";
const nickname: string | null = null;

export const examples = [
  { code: 'count && "あり"', result: count && "あり" },
  { code: 'stock && "あり"', result: stock && "あり" },
  { code: 'keyword || "デフォルト"', result: keyword || "デフォルト" },
  { code: 'count || "デフォルト"', result: count || "デフォルト" },
  { code: 'count ?? "デフォルト"', result: count ?? "デフォルト" },
  { code: 'nickname ?? "デフォルト"', result: nickname ?? "デフォルト" },
];

これが「画面に 0 が出る」の正体

React では、条件によって表示を出し分けるときにこう書きます。

{items.length && <p>{items.length} 件あります</p>}

件数が 3 なら、3 && <p>...</p> で右側が返り、 文章が表示されます。ここまでは意図どおりです。

ところが件数が 0 のとき、この式は 0 を返します。そして React は、数値の 0 を画面に表示します。 非表示にしたかったのに、0 という文字が残るのはこのためです。

// ✕ 0 が画面に出る
{items.length && <p>{items.length} 件あります</p>}

// ○ 比較して true / false にしてから渡す
{items.length > 0 && <p>{items.length} 件あります</p>}

false は React が画面に出さないと決めている値なので、 何も表示されません。

operators.ts

/*
  && は 左が偽ならその左の値を返し、真なら右の値を返す
  || は 左が真ならその左の値を返し、偽なら右の値を返す
  ?? は 左が null か undefined のときだけ右の値を返す
*/

// 実際のデータと同じように、変数に入った値で試す
const count: number = 0;
const stock: number = 5;
const keyword: string = "";
const nickname: string | null = null;

export const examples = [
  { code: 'count && "あり"', result: count && "あり" },
  { code: 'stock && "あり"', result: stock && "あり" },
  { code: 'keyword || "デフォルト"', result: keyword || "デフォルト" },
  { code: 'count || "デフォルト"', result: count || "デフォルト" },
  { code: 'count ?? "デフォルト"', result: count ?? "デフォルト" },
  { code: 'nickname ?? "デフォルト"', result: nickname ?? "デフォルト" },
];

|| と ?? の使い分け

初期値を用意したいとき、つい || を使ってしまいますが、 これも 0 で事故が起きます。

const count = 0;

count || 10; // 10  ← 0 は偽なので置き換えられてしまう
count ?? 10; // 0   ← null / undefined でないのでそのまま

「値が入っていないときだけ初期値を使いたい」のであれば、?? を使うのが正解です。|| は 0 や空文字まで「なかったこと」にしてしまいます。

operators.ts

/*
  && は 左が偽ならその左の値を返し、真なら右の値を返す
  || は 左が真ならその左の値を返し、偽なら右の値を返す
  ?? は 左が null か undefined のときだけ右の値を返す
*/

// 実際のデータと同じように、変数に入った値で試す
const count: number = 0;
const stock: number = 5;
const keyword: string = "";
const nickname: string | null = null;

export const examples = [
  { code: 'count && "あり"', result: count && "あり" },
  { code: 'stock && "あり"', result: stock && "あり" },
  { code: 'keyword || "デフォルト"', result: keyword || "デフォルト" },
  { code: 'count || "デフォルト"', result: count || "デフォルト" },
  { code: 'count ?? "デフォルト"', result: count ?? "デフォルト" },
  { code: 'nickname ?? "デフォルト"', result: nickname ?? "デフォルト" },
];

理解できたか確かめる

確認クイズ

0 && '表示' の結果は?

確認クイズ

件数が 0 のときは何も表示したくない。正しい書き方は?

確認クイズ

const count = 0; のとき、count ?? 10 の結果は?

operators.ts

/*
  && は 左が偽ならその左の値を返し、真なら右の値を返す
  || は 左が真ならその左の値を返し、偽なら右の値を返す
  ?? は 左が null か undefined のときだけ右の値を返す
*/

// 実際のデータと同じように、変数に入った値で試す
const count: number = 0;
const stock: number = 5;
const keyword: string = "";
const nickname: string | null = null;

export const examples = [
  { code: 'count && "あり"', result: count && "あり" },
  { code: 'stock && "あり"', result: stock && "あり" },
  { code: 'keyword || "デフォルト"', result: keyword || "デフォルト" },
  { code: 'count || "デフォルト"', result: count || "デフォルト" },
  { code: 'count ?? "デフォルト"', result: count ?? "デフォルト" },
  { code: 'nickname ?? "デフォルト"', result: nickname ?? "デフォルト" },
];

この章のまとめ

  • 偽として扱われるのは false, 0, "", null, undefined, NaN の 6 つだけ。空の配列やオブジェクトは
  • &&|| は true / false ではなく値そのものを返す
  • {count && <p>…</p>} は、count が 0 のとき画面に 0 を出す。count > 0 && と書けば防げる
  • 初期値を入れたいときは || ではなく ??

falsy.ts

// 偽として扱われる値は、この 6 つだけ
export const falsyValues = [false, 0, "", null, undefined, NaN];

// それ以外は全部「真」として扱われる。
// 中身が空でも、文字が "0" や "false" でも真になる
export const truthyButSurprising = [[], {}, "0", "false", -1, " "];