no-undef
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に「これはグローバル変数として存在しますよ」と伝えることで、エラーを回避できます。
-
/*global ... */ コメントによる宣言
/*global someFunction, bar*/ /*eslint no-undef: "error"*/ const foo = someFunction(); bar = 10;
このコメントは、
someFunction
とbar
がこのファイル内でグローバル変数として利用可能であることをESLintに伝えます。bar:true
のように記述することで、その変数が書き込み可能であることも示せます(デフォルトでは読み取り専用)。 -
.eslintrc ファイルでの globals 設定
プロジェクト全体で特定のグローバル変数を使う場合、.eslintrc
ファイルに以下のように記述できます。{ "globals": { "someFunction": "readonly", "bar": "writable" }, "rules": { "no-undef": "error" } }
"readonly"
は読み取り専用、"writable"
は書き込み可能を意味します。 -
env 設定による環境定義
ブラウザ環境(window
,document
など)やNode.js環境(require
,module
など)のように、特定の実行環境にデフォルトで存在するグローバル変数については、env
設定を使用するのが一般的です。{ "env": { "browser": true, // ブラウザ環境のグローバル変数を有効化 "node": true // Node.js環境のグローバル変数を有効化 }, "rules": { "no-undef": "error" } }
これにより、
setTimeout
やalert
(ブラウザ)、require
やmodule.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
を使用している場合は、このルールを無効にすることが推奨されています。
グローバル変数が未定義と判断される
ブラウザ環境の window
や document
、Node.js 環境の require
や module
など、特定の実行環境に存在するグローバル変数が 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" } }
- Jest の場合
意図しないグローバル変数の作成またはタイポ
これが最も一般的なケースであり、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: true
をenv
に設定することで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)
説明
outerVar
は outerFunction
内で宣言されているため、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 の機能を最大限に活用することが最適な「代替」方法となります。