no-undef

2025-05-31

ESLintにおけるno-undefルールは、JavaScriptコード内で未定義の変数を使用することを禁止するためのものです。

もう少し詳しく説明すると、以下のようになります。

no-undefとは?

このルールは、コード内で宣言されていない変数や、スコープ外の変数が参照された場合にエラー(または警告)を発生させます。

例えば、以下のようなコードがあるとします。

const message = "Hello";
console.log(greeting); // ここで 'greeting' は定義されていない

この場合、no-undefルールが有効になっていると、greetingが未定義であることをESLintが検出し、エラーを報告します。これは、タイポ(typo)や、グローバル変数を意図せず使用してしまっているといった、潜在的なReferenceError(参照エラー)を防ぐのに役立ちます。

なぜ重要なのか?

  • グローバル汚染の防止: 意図せずグローバル変数を生成してしまう(例えば、varキーワードをつけ忘れて変数を宣言した場合など)ことを防ぎます。
  • コードの可読性向上: 変数がどこで定義されているかを明確にすることで、コードの読みやすさと理解しやすさが向上します。
  • バグの早期発見: 未定義の変数を使用すると、実行時にReferenceErrorが発生し、アプリケーションがクラッシュする可能性があります。no-undefは、コードを実行する前にこれらの問題を特定し、修正するのに役立ちます。

no-undefの適用例

不正なコードの例(no-undefがエラーを報告)

/*eslint no-undef: "error"*/
const foo = someFunction(); // 'someFunction' は定義されていない
bar = 10;                  // 'bar' は定義されていない

正しいコードの例(no-undefがエラーを報告しない)

未定義に見える変数でも、ESLintに「これはグローバル変数として存在しますよ」と伝えることで、エラーを回避できます。

  1. /*global ... */ コメントによる宣言

    /*global someFunction, bar*/
    /*eslint no-undef: "error"*/
    const foo = someFunction();
    bar = 10;
    

    このコメントは、someFunctionbarがこのファイル内でグローバル変数として利用可能であることをESLintに伝えます。bar:trueのように記述することで、その変数が書き込み可能であることも示せます(デフォルトでは読み取り専用)。

  2. .eslintrc ファイルでの globals 設定
    プロジェクト全体で特定のグローバル変数を使う場合、.eslintrcファイルに以下のように記述できます。

    {
      "globals": {
        "someFunction": "readonly",
        "bar": "writable"
      },
      "rules": {
        "no-undef": "error"
      }
    }
    

    "readonly"は読み取り専用、"writable"は書き込み可能を意味します。

  3. env 設定による環境定義
    ブラウザ環境(window, documentなど)やNode.js環境(require, moduleなど)のように、特定の実行環境にデフォルトで存在するグローバル変数については、env設定を使用するのが一般的です。

    {
      "env": {
        "browser": true, // ブラウザ環境のグローバル変数を有効化
        "node": true     // Node.js環境のグローバル変数を有効化
      },
      "rules": {
        "no-undef": "error"
      }
    }
    

    これにより、setTimeoutalert(ブラウザ)、requiremodule.exports(Node.js)などが未定義エラーとして報告されなくなります。

typeof オプション

no-undefルールにはtypeofというオプションもあります。デフォルトではfalseですが、これをtrueに設定すると、typeof演算子で未定義の変数をチェックしている場合でも警告を発するようになります。

/*eslint no-undef: ["error", { "typeof": true }] */
if (typeof UndefinedIdentifier === "undefined") { // 'UndefinedIdentifier' が未定義のためエラー
  // 何らかの処理
}

このオプションをtrueにすると、未宣言の変数に対してtypeofチェックを使用するのを防ぎたい場合に便利です。

TypeScriptを使用している場合、TypeScriptコンパイラ自身が未定義の変数をチェックするため、no-undefルールは不要になることがほとんどです。そのため、@typescript-eslint/eslint-pluginを使用している場合は、このルールを無効にすることが推奨されています。



グローバル変数が未定義と判断される

ブラウザ環境の windowdocument、Node.js 環境の requiremodule など、特定の実行環境に存在するグローバル変数が no-undef のエラーとして報告されることがあります。

エラー例

console.log(window.location); // 'window' is not defined. (no-undef)
const fs = require('fs');    // 'require' is not defined. (no-undef)

トラブルシューティング

  • ファイル内での /*global ... */ コメント
    特定のファイルでのみグローバル変数を宣言したい場合、ファイルの先頭にコメントを追加します。
    /*global $, _ :true*/
    // eslint-disable-next-line no-undef
    console.log($('div'));
    _ = 123; // _はwritableで宣言されているため問題なし
    
  • globals オプションの利用
    特定のライブラリやフレームワークが提供するグローバル変数(例: jQuery の $、Underscore.js の _、React の React など)をESLintに認識させるには、globals オプションを使用します。
    // .eslintrc.json
    {
      "globals": {
        "$": "readonly",   // jQueryの$が読み取り専用であることを宣言
        "_": "readonly",   // Underscore.jsの_が読み取り専用であることを宣言
        "React": "writable" // Reactが書き込み可能であることを宣言
      },
      "rules": {
        "no-undef": "error"
      }
    }
    
    • "readonly" はその変数が読み取り専用であることを示し、代入しようとすると no-global-assign ルール(有効な場合)によってエラーになります。
    • "writable" はその変数が代入可能であることを示します。
  • env オプションの利用
    ESLintの設定ファイル(.eslintrc.js または .eslintrc.json)で、コードが実行される環境を指定します。
    • ブラウザ環境の場合
      // .eslintrc.json
      {
        "env": {
          "browser": true // window, documentなどのブラウザグローバルを有効にする
        },
        "rules": {
          "no-undef": "error"
        }
      }
      
    • Node.js 環境の場合
      // .eslintrc.json
      {
        "env": {
          "node": true // require, module, processなどのNode.jsグローバルを有効にする
        },
        "rules": {
          "no-undef": "error"
        }
      }
      
    • 両方の場合
      // .eslintrc.json
      {
        "env": {
          "browser": true,
          "node": true
        },
        "rules": {
          "no-undef": "error"
        }
      }
      

テストフレームワークのグローバル変数が未定義と判断される

Jest, Mocha, Jasmine などのテストフレームワークは、describe, it, expect, beforeEach などの独自のグローバル関数を提供します。これらが no-undef のエラーとして報告されることがあります。

エラー例

describe('My test suite', () => { // 'describe' is not defined. (no-undef)
  it('should do something', () => { // 'it' is not defined. (no-undef)
    expect(true).toBe(true);       // 'expect' is not defined. (no-undef)
  });
});

トラブルシューティング

  • overrides を利用してテストファイルのみに適用
    通常のコードにはテストフレームワークのグローバル変数は不要なので、overrides を使ってテストファイルにのみ env 設定を適用するのがベストプラクティスです。
    // .eslintrc.json
    {
      "env": {
        "browser": true,
        "node": true
      },
      "overrides": [
        {
          "files": ["**/*.test.js", "**/*.spec.js"], // テストファイルのパターン
          "env": {
            "jest": true
          }
        }
      ],
      "rules": {
        "no-undef": "error"
      }
    }
    
  • env オプションでテスト環境を指定
    各テストフレームワークに対応する env オプションがあります。
    • Jest の場合
      // .eslintrc.json
      {
        "env": {
          "jest": true // Jestのグローバル変数を有効にする
        },
        "rules": {
          "no-undef": "error"
        }
      }
      
    • Mocha の場合
      // .eslintrc.json
      {
        "env": {
          "mocha": true // Mochaのグローバル変数を有効にする
        },
        "rules": {
          "no-undef": "error"
        }
      }
      

意図しないグローバル変数の作成またはタイポ

これが最も一般的なケースであり、no-undef ルールの本来の目的です。変数の宣言を忘れていたり、変数名にタイプミスがあったりする場合に発生します。

エラー例

function greet() {
  message = "Hello"; // 'message' is not defined. (no-undef) - var/let/const がない
  console.log(mesage); // 'mesage' is not defined. (no-undef) - タイポ
}

トラブルシューティング

  • 変数名の確認
    タイプミスがないか、変数名が正しく記述されているかを確認します。
  • var, let, const の使用を確認
    変数を使用する前に必ず宣言されているかを確認します。
    function greet() {
      const message = "Hello"; // const で宣言
      console.log(message);    // 正しい変数名を使用
    }
    

webpackなどのバンドラーが提供するグローバル変数

トラブルシューティング

  • globals オプションでの宣言
    // .eslintrc.json
    {
      "globals": {
        "process": "readonly" // process.env.NODE_ENVなどのために
      },
      "rules": {
        "no-undef": "error"
      }
    }
    
    多くの場合、node: trueenv に設定することで process オブジェクトは自動的に認識されます。

TypeScript を使用している場合

TypeScript プロジェクトでは、TypeScript コンパイラ自体が強力な型チェックと未定義変数チェックを行うため、no-undef ルールは不要になることがほとんどです。むしろ、TypeScript の型情報と競合して誤検知を引き起こす可能性があります。

  • IDE/エディタの ESLint 連携
    VS Code などのエディタで ESLint プラグインを使用している場合、その設定やキャッシュが原因で問題が発生することもあります。エディタの再起動やプラグインの再インストールを試すことも有効です。
  • ESLint のバージョンとルールの変更
    ESLint のバージョンアップによってルールの挙動や推奨される設定方法が変わることがあります。公式ドキュメントを確認すると良いでしょう。
  • キャッシュの問題
    ESLint が以前のキャッシュを使用して誤った結果を表示している可能性があります。eslint --fix --cache-location .eslintcache のようにキャッシュを明示的に指定したり、キャッシュファイルを削除してから再実行してみたりしてください。
  • ESLint の設定ファイルの場所
    .eslintrc.js, .eslintrc.json, .eslintrc.yml など、設定ファイルがプロジェクトの正しい場所にあるか確認してください。ESLint は親ディレクトリを探索します。


基本的な未定義変数の例 (エラーとなるケース)

最も一般的な no-undef のケースは、変数を使う前に宣言していない場合です。

// 例 1-1: 変数宣言の忘れ
function greet() {
  message = "Hello, world!"; // 'message' がどこにも宣言されていない
  console.log(message);
}

greet();

ESLint の出力 (想定)

/path/to/your/file.js
  3:3  error  'message' is not defined  no-undef

 1 problem (1 error, 0 warnings)

説明
このコードでは、message 変数を var, let, const のいずれかで宣言することなく直接使用しています。ESLint はこれを未定義の変数と判断し、no-undef エラーを報告します。

修正方法
変数を適切に宣言します。

// 例 1-1: 修正後
function greet() {
  const message = "Hello, world!"; // const で宣言
  console.log(message);
}

greet();

タイプミス (typo) による未定義変数の例 (エラーとなるケース)

変数名にタイプミスがあった場合も、no-undef エラーが発生します。ESLint は別の変数として認識するためです。

// 例 2-1: 変数名のタイプミス
const userName = "Alice";
console.log(usernName); // 'userName' のつもりが 'usernName' となっている

ESLint の出力 (想定)

/path/to/your/file.js
  2:13  error  'usernName' is not defined  no-undef

 1 problem (1 error, 0 warnings)

説明
userName は宣言されていますが、console.log 内で参照されているのは usernName という別の(未定義の)変数です。

修正方法
正しい変数名を使用します。

// 例 2-1: 修正後
const userName = "Alice";
console.log(userName); // 正しい変数名

グローバル変数が未定義と判断される例 (エラーとなるケース)

ブラウザ環境や Node.js 環境にデフォルトで存在するグローバル変数(window, document, console, require など)が、ESLint の設定によっては未定義と判断されることがあります。

// 例 3-1: ブラウザ環境のグローバル変数が未定義と判断される
document.getElementById('my-element'); // 'document' is not defined
alert('Hello!'); // 'alert' is not defined
// 例 3-2: Node.js 環境のグローバル変数が未定義と判断される
const path = require('path'); // 'require' is not defined
console.log(__dirname); // '__dirname' is not defined

ESLint の出力 (想定 例 3-1)

/path/to/your/file.js
  1:1  error  'document' is not defined  no-undef
  2:1  error  'alert' is not defined     no-undef

 2 problems (2 errors, 0 warnings)

説明
ESLint はデフォルトでは、これらの環境固有のグローバル変数を認識していません。

修正方法
ESLint の設定ファイル (.eslintrc.json など) で、コードが実行される環境 (env) を指定します。

  • 特定のファイル内でのみグローバル変数を指定する場合 (/*global ... */ コメント)

    // 例 3-3: ファイル先頭のコメントでグローバル変数を宣言
    /*global MyGlobalVar:true, AnotherGlobalFunction*/
    
    MyGlobalVar = 10;          // MyGlobalVar は書き込み可能として宣言
    AnotherGlobalFunction(); // AnotherGlobalFunction は読み取り専用として宣言
    
  • 特定のライブラリのグローバル変数を指定する場合
    例えば、jQuery の $ や Lodash の _ など、アプリケーション固有のグローバル変数がある場合は、globals オプションを使用します。

    // .eslintrc.json
    {
      "globals": {
        "$": "readonly", // jQuery の $ が読み取り専用であることを宣言
        "_": "readonly"  // Lodash の _ が読み取り専用であることを宣言
      },
      "rules": {
        "no-undef": "error"
      }
    }
    
    • "readonly": 変数が読み取り専用であることを示します。
    • "writable": 変数が書き込み可能であることを示します。
  • Node.js 環境の場合

    // .eslintrc.json
    {
      "env": {
        "node": true // 'require', 'module', '__dirname', '__filename' などを有効にする
      },
      "rules": {
        "no-undef": "error"
      }
    }
    
  • // .eslintrc.json
    {
      "env": {
        "browser": true // 'window', 'document', 'alert' などを有効にする
      },
      "rules": {
        "no-undef": "error"
      }
    }
    

スコープ外の変数を参照する例 (エラーとなるケース)

JavaScript では、変数は宣言されたスコープ内でしかアクセスできません。異なるスコープから変数を参照しようとすると、no-undef エラーが発生します。

// 例 4-1: スコープ外の変数参照
function outerFunction() {
  const outerVar = "I am outer";
}

function innerFunction() {
  console.log(outerVar); // 'outerVar' は outerFunction のスコープ内でのみ有効
}

outerFunction();
innerFunction();

ESLint の出力 (想定)

/path/to/your/file.js
  7:15  error  'outerVar' is not defined  no-undef

 1 problem (1 error, 0 warnings)

説明
outerVarouterFunction 内で宣言されているため、innerFunction からはアクセスできません。

修正方法
変数を参照したいスコープ内で変数を宣言するか、関数引数として渡します。

// 例 4-1: 修正後 (変数を同じスコープで宣言)
const outerVar = "I am outer"; // グローバルスコープで宣言

function outerFunction() {
  // outerVar にアクセス可能
}

function innerFunction() {
  console.log(outerVar); // outerVar にアクセス可能
}

outerFunction();
innerFunction();
// 例 4-1: 修正後 (引数として渡す)
function outerFunction() {
  const outerVar = "I am outer";
  innerFunction(outerVar); // innerFunction に引数として渡す
}

function innerFunction(data) { // 引数を受け取る
  console.log(data);
}

outerFunction();

TypeScript プロジェクトでは、TypeScript コンパイラ自体が強力な型チェックと未定義変数チェックを行うため、ESLint の no-undef ルールは不要になることがほとんどです。むしろ、ESLint と TypeScript の間で重複チェックや競合が発生する可能性があります。

ESLint の出力 (TypeScript での想定)
通常、TypeScript コンパイラが先にエラーを報告します。ESLint の no-undef が有効なままだと、以下のようなエラーが出る可能性があります。

// 例 5-1: TypeScript コードで no-undef が有効なままの場合
const value: string = someUndefinedVar; // 'someUndefinedVar' is not defined (ESLint no-undef)
                                       // TS2304: Cannot find name 'someUndefinedVar'. (TypeScript)

トラブルシューティング
TypeScript プロジェクトでは、@typescript-eslint/eslint-plugin を使用し、no-undef ルールを無効にすることが推奨されます。このプラグインは、TypeScript の型情報を活用して、より正確なチェックを提供します。

// .eslintrc.json (TypeScript プロジェクトの推奨設定)
{
  "parser": "@typescript-eslint/parser", // TypeScript のパーサーを指定
  "parserOptions": {
    "ecmaVersion": 2020,
    "sourceType": "module"
  },
  "plugins": [
    "@typescript-eslint"
  ],
  "extends": [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended" // TypeScript 向けの推奨ルールセットを継承
  ],
  "rules": {
    "no-undef": "off" // TypeScript を使用しているため無効化する
  }
}


ESLint の no-undef ルールは、未定義の変数の使用を防ぐための基本的ながら非常に重要なルールです。このルール自体を完全に「代替する」というよりは、no-undef がカバーする範囲を別の方法で補強したり、より高度な方法で同じ問題を解決したりすると考えるのが適切です。

特にTypeScriptを使用している場合は、no-undefルールとは異なるアプローチで未定義変数チェックが行われます。

ここでは、no-undefに関連する目的(未定義変数や参照エラーの防止)を達成するための代替方法や、no-undefを補完する方法について説明します。

TypeScript による静的型チェック

最も強力かつ推奨される代替(または上位)方法です。TypeScript は JavaScript に静的型付けを導入することで、コードのコンパイル時に様々なエラー(未定義変数を含む)を検出します。

どのように機能するか: TypeScript コンパイラは、コード内のすべての識別子(変数、関数、クラスなど)が宣言されているか、またはグローバルスコープで利用可能であるかを厳密にチェックします。宣言されていない識別子を使用しようとすると、コンパイル時にエラーを報告します。


// TypeScript ファイル (.ts)
const message: string = "Hello";
console.log(greeting); // TypeScript コンパイラが 'Cannot find name 'greeting'.' エラーを報告

no-undef との関連性: TypeScript プロジェクトでは、@typescript-eslint/eslint-plugin を使用する場合、通常 no-undef ルールは無効化されます。これは、TypeScript コンパイラ自身がこの種のチェックを行うため、ESLint の no-undef を有効にしておくと重複してエラーが出たり、TypeScript の型推論によって生成されたコードで誤検知が発生したりするためです。

利点:

  • リファクタリングの安全性
    変数名の変更などのリファクタリング時に、参照先の更新漏れによる未定義エラーを防ぎやすくなります。
  • IDE サポート
    多くの IDE(VS Codeなど)で TypeScript の型チェックが統合されており、リアルタイムでエラーをフィードバックしてくれます。
  • より強力なチェック
    型情報に基づいて、より複雑な未定義エラーや型関連のエラーも検出できます。

JavaScript の厳格モード("use strict")

JavaScript の厳格モードは、安全でないアクションを実行することを防ぐために、特定の構文上の制限を適用します。その一つに、未宣言の変数への代入を禁止するというものがあります。

どのように機能するか: スクリプトの先頭、または関数の先頭に 'use strict'; を記述することで、厳格モードを有効にできます。厳格モードでは、var, let, const で宣言されていない変数に値を代入しようとすると、ReferenceError がスローされます。


'use strict';

function doSomething() {
  undeclaredVar = 10; // ReferenceError: undeclaredVar is not defined
}

doSomething();

// ESLintのno-undefもここでエラーを出すが、厳格モードは実行時にエラーを発生させる。

no-undef との関連性:

  • ESLint は、厳格モードが有効であっても、未定義変数への代入を静的に検出できるため、no-undef が依然として価値を持ちます。厳格モードは、ブラウザやNode.jsが対応していれば、ESLintが導入されていない環境でもこの種のバグを防ぐ最終防衛線となり得ます。
  • 厳格モードは実行時にエラーを発生させます。
  • no-undef静的解析ツールであり、コードを実行する前に未定義変数を検出します。

利点:

  • 古い JavaScript コードベースで、ESLint の導入が難しい場合に一時的な対策となる。
  • ESLint などのツールがなくても、実行時に未定義変数への代入によるグローバル汚染を防ぐ。

JSDoc を使った型アノテーションとツールによるチェック

JSDoc を使って JavaScript コードに型情報のアノテーションを付けることで、TypeScript のような静的型チェックツール(VS Code の TypeScript 言語サービスなど)がその情報を利用して、未定義変数や型ミスマッチを検出できるようになります。

どのように機能するか: JSDoc コメントで変数の型を @type タグで指定したり、関数の引数や戻り値の型を指定したりします。ESLint 自体は JSDoc の型情報を直接使って no-undef を解決するわけではありませんが、VS CodeなどのエディタはこれらのJSDocコメントを解析し、TypeScriptの型チェック機能のように振る舞って未定義変数を検出してくれます。


/**
 * @param {string} name
 * @returns {string}
 */
function sayHello(name) {
  // name はJSDocで定義されているため、VS Codeなどは認識する
  console.log(greetMessage); // JSDocだけでは未定義は検出されないが、VS CodeのTypeScript言語サービスは警告を出す可能性がある
  return `Hello, ${name}!`;
}

sayHello("World");

no-undef との関連性: これは no-undef の直接の代替ではありませんが、JSDocとエディタの組み合わせによって、TypeScriptほど厳密ではないものの、コード補完や一部の基本的なチェックにおいて no-undef が捉える問題の一部を補完的に支援できます。主に、エディタのフィードバックが向上する点が大きいです。

利点:

  • コードのドキュメント化にも役立つ。
  • TypeScriptへの移行なしに、一部の静的チェックの恩恵を受けられる。

これは特定のツールや言語機能の代替ではありませんが、最終的な防衛線として重要です。

どのように機能するか:

  • 単体テスト / 統合テスト
    コードが実行されるテスト環境で、未定義変数による ReferenceError が発生するかどうかを確認します。
  • コードレビュー
    経験豊富な開発者がコードをレビューし、未定義変数の使用のような単純なエラーを見つけることができます。

no-undef との関連性: no-undef は開発の早い段階でエラーを検出するのに役立ちますが、コードレビューやテストは、ESLint が見逃した(あるいは設定ミスなどで見つけられなかった)問題を発見したり、より複雑なロジックエラーや実行時の問題を特定するために不可欠です。

利点:

  • 実行時の問題(ESLintでは検出できないもの)も発見できる。
  • ツールの設定に依存せず、人間による包括的なチェックが可能。

ESLint の no-undef ルールは、JavaScript コードの静的解析において未定義変数を検出するための非常に効果的な方法です。JavaScript プロジェクトでは、no-undef を適切に設定して活用することが推奨されます。

しかし、もし TypeScript を使用しているのであれば、TypeScript コンパイラが no-undef と同等以上のチェックを型システムを通じて提供するため、ESLint の no-undef ルールを無効化し、TypeScript の機能を最大限に活用することが最適な「代替」方法となります。