[JavaScript] セミコロン付け忘れリスクの参考例

7 min read

セミコロン省略とASI

JavaScriptはセミコロン(;)を用いて文を終了させる言語である。しかし、セミコロンが省略された場合でもJavaScriptエンジンはAutomatic Semicolon Insertion(ASI)という機能によって、コードの適切な場所にセミコロンを自動的に挿入されるため、問題なくコードは実行される。

開発者の中には、この特性を活用してセミコロンを省略する書き方を好む者もいる。しかしながら、セミコロンの省略挟まざまな問題を引き起こす可能性がある。

問題の例

ASIが引き起こす予期せぬ結果

ASIが開発者の意図とは異なる動作を引き起こすことある。

以下はその一例である。

let a = 5
let b = 10
let c = 20
[a, b] = [b, a]

上記のコードでは、abの値を交換するつもりで書かれている。しかしながら、セミコロンが省略されたために、JavaScriptエンジンは以下のようにこのコードを解釈する。

let a = 5
let b = 10
let c = 20[a, b] = [b, a]

これにより、cが未定義のエラーが発生する。これはJavaScriptエンジンが20[a, b]をひとつの文と解釈した結果である。

return文とセミコロンの重要性

return文の直後に値を返さないケースもある。

function getValue() {
    return 
        { message: "Hello, world!" };
}

この関数は意図とは異なり、期待するオブジェクト({ message: "Hello, world!" })ではなくundefinedを返す。これはASIによって、return文と{ message: "Hello, world!" }が別々の文として解釈されるためである(return;と解釈されているため、後続が評価されない)。

この問題を回避するには、return文の後ろに直接値を返す形にするか、または適切にセミコロンを使用する必要がある。

function getValue() {
  return { message: 'Hello, world!' };
}

IIFEとセミコロンの必要性

JavaScriptでは、即時関数(Immediately Invoked Function Expression: IIFE)というパターンが頻繁に使用される。しかし、セミコロンを省略すると、IIFEの前に書かれたコードが影響を受ける可能性がある。

その一例が以下の通り。

let name = "Dog"
(function() {
    console.log(name)
})()

上記のコードの場合、JavaScriptエンジンはこれを以下のように解釈するためエラーが発生する。

let name = "Dog"(function() {
    console.log(name)
})()

これを避けるためには、IIFEの前にはセミコロンを配置すべきである。

let name = 'Dog';
(function () {
  console.log(name);
})();

インクリメント/デクリメント演算子との不整合

インクリメント(++)やデクリメント(--)演算子との間で予期せぬ問題が発生することもある。

その一例が以下の通り。

let x = 5
let y = 10
x
++y

このコードでは、yをインクリメントしようとしているが、セミコロンの省略により、JavaScriptエンジンは以下のように解釈してしまう。

let x = 5
let y = 10
x++y

トランスパイラ(TypeScriptやBabel)

セミコロンを含むJavaScriptの文法問題を解決するひとつの手段として、TypeScriptやBabelといったトランスパイラの利用が考えられる。これらのツールは、JavaScriptの新しいバージョンまたは拡張機能を古いバージョンに変換(トランスパイル)するものである。

  • TypeScriptの場合
    TypeScriptでは、JavaScriptと同じく末尾のセミコロンは省略可能である。しかし、TypeScriptはJavaScriptよりも厳格な型検査が行われるため、セミコロンの省略がエラーを引き起こすことは少ない。
    ESLintでは、セミコロンの使用に関する設定を指定でき、その設定にしたがってセミコロンの挿入や削除を自動的に行うことが可能。
  • Babelの場合
    Babel自体はセミコロンの存在に関しては無関心であるが、Babelとともに使用されるlinterやフォーマッタ(例えばESLintやPrettier)は、コードスタイルの一部としてセミコロンの使用を強制できる。

これらのツールを使用しているのであれば、セミコロンの省略によるエラーは大幅に減らすことできている。しかし、セミコロンの使用に関する問題は存在している。

ESLint

ESLintを利用することでセミコロンの付け忘れなどに対する警告や自動修正ができる。

設定ファイル

// .eslintrc.json
{
  "semi": ["error", "always"],
  "semi-spacing": ["error", { "after": true, "before": false }],
  "semi-style": ["error", "last"],
  "no-extra-semi": "error",
  "no-unexpected-multiline": "error",
  "no-unreachable": "error"
}

ルール概要

ルール名説明
semiASI の代わりにセミコロンを必須または禁止する
semi-spacingセミコロンの前後に一貫したスペースを強制する
semi-styleセミコロンの位置を強制する
no-extra-semi不要なセミコロンを禁止する
no-unexpected-multiline紛らわしい複数行の式を禁止する
no-unreachablereturnthrowcontinuebreakステートメントの後の到達不能なコードを禁止する

まとめ

セミコロンを省略することでコードの動作が予期せぬ方法で変わる可能性がある。開発者としては、これらの問題を理解し、適切な対策を講じることが重要である。常にセミコロンを用いることで、予期せぬエラーを避け、コードの可読性と安定性を向上させることが可能である。