厳密等価演算子(===)と等価演算子(==)の違い ― JavaScript で == を使わない方がいい理由

結論

JavaScript で値を比較するときは、=== を使う== は型が違う値を比較する際に「型強制」を行うため、直感に反する結果になりやすい。実プロジェクトでは ESLint の eqeqeq ルールで == を禁止しているケースが多い。

0 == ""        // true("" は 0 に変換される)
0 == "0"       // true("0" は 0 に変換される)
0 == false     // true(false は 0 に変換される)
"" == false    // true
[] == false    // true([] は "" に変換され、さらに 0 に)
null == undefined  // true(特別ルール)
"1" == true    // true(true は 1、"1" も 1)

0 === ""       // false
0 === "0"      // false
0 === false    // false
[] === false   // false
null === undefined  // false
"1" === true   // false

== は仕様の中に「型変換のルール」が10行以上ある複雑な演算子。覚えるより、=== を使う方が安全。


===== の違いを一行で

  • ===(厳密等価): 型も値も完全に一致したときだけ true
  • ==(等価): 型が違ったら片方を変換してから比較。値が一致したら true
// 型が同じなら ===  と == は同じ結果
1 === 1     // true
1 == 1      // true
"a" === "a" // true
"a" == "a"  // true

// 型が違うと結果が分かれる
1 === "1"   // false(型が違うので即 false)
1 == "1"    // true("1" を 1 に変換してから比較)

型強制で起こる典型的な落とし穴

1. 空文字と 0 が等しい

"" == 0          // true
"" == false      // true
0 == false       // true
0 == "0"         // true
"" == "0"        // false ← これは仕様の例外

最後の "" == "0"両方が文字列のため型変換が起きず、単純な文字列比較で false になる。型が違うと変換するが、同じ型なら変換しない、というのが == のルール。同じ "0っぽい" 値同士でも結果が変わる。

2. null と undefined だけは特別扱い

null == undefined   // true(特別ルール)
null == null        // true
undefined == undefined // true

null == 0           // false
null == false       // false
null == ""          // false

undefined == 0      // false
undefined == false  // false

nullundefined互いにだけ等しい0false とは等しくない。これは ECMAScript 仕様で明示的に決められたルール。

3. 配列・オブジェクトとの比較

[] == false    // true([] → "" → 0、false → 0)
[0] == false   // true([0] → "0" → 0)
[] == 0        // true
[] == ""       // true
[1] == 1       // true([1] → "1" → 1)
[1, 2] == "1,2" // true(配列の toString は "1,2")

{} == false    // false({} は "[object Object]" になる)

オブジェクトと比較するとき、==valueOf() または toString() を呼んでプリミティブに変換する。配列の toString() はカンマ区切り文字列を返すので、[1] == 1 が成立してしまう。


どんな時でも === で問題ないか

ほぼ全ての場面で === を使うべき。例外として意図的に nullundefined を同じものとして扱いたいケースがあるが、その場合も明示的に書く方が読みやすい。

// NG: 意図が不明
if (value == null) { /* null か undefined */ }

// OK: 意図が明示的
if (value === null || value === undefined) { /* null か undefined */ }

// 最近は Nullish 演算子で簡潔に
const x = value ?? defaultValue  // value が null/undefined なら defaultValue

ESLint で == を禁止する

実プロジェクトでは eqeqeq ルールを設定して == を禁止するのが普通。

{
  "rules": {
    "eqeqeq": ["error", "always"]
  }
}

"always" は例外なし全面禁止。"smart" にすると null との比較だけは例外扱いになる(x == nullnullundefined を両方判定)。コードベースの方針による。

TypeScript 環境なら @typescript-eslint/eqeqeq でも同じことができる。


TypeScript ではほぼ無関係?

TypeScript では型が一致しない値同士の == 比較がコンパイルエラーになるため、== で型強制に頼ろうとするとそもそもビルドが通らない。TypeScript なら === 一択

const x: string = "1"
const y: number = 1

x == y   // TS Error: This comparison appears to be unintentional
x === y  // TS Error: 同じく

ただし、unknownany 型と比較する場合は型チェックを抜けるため、TypeScript でも eqeqeq ルールを併用しておくと安全。


まとめ

  • === は型と値の両方を比較。ほぼ全てのケースで === を使う
  • == は型が違うと片方を変換するため、空文字 == 0、null == undefined のような直感に反する結果が出る
  • 配列やオブジェクトとの == 比較は特に予測不能([] == false が true)
  • ESLint の eqeqeq ルールで == を全面禁止しておくと安全
  • TypeScript では型不一致の == がコンパイルエラーになるため、自然に === を使うことになる

関連リンク

関連記事