ESLint no-magic-numbersとは?JavaScript可読性向上のための解説
「no-magic-numbers」は、プログラミングのコード中に直接記述された、意味が文脈から明らかでない数値を禁止する「ESLint」のルールのことです。これらの数値は「マジックナンバー」と呼ばれます。
なぜマジックナンバーが問題なのでしょうか?
- 意図の不明確さ
その数値がなぜその値になっているのか、背景にある理由がコードから読み取れません。 - 保守性の低下
もしその数値の意味が変わった場合、コード全体に散らばっている同じ数値をすべて探し出して修正する必要があります。これは非常に手間がかかり、ミスも起こりやすくなります。 - 可読性の低下
コードを見たときに、その数値が何を意味するのかすぐに理解できません。例えば、if (count > 7)
と書かれていても、「7」が何を指しているのか推測するしかありません。
「no-magic-numbers」ルールを有効にするとどうなるの?
このルールを有効にした状態で、意味が明確でない数値を直接コードに記述すると、「ESLint」が警告やエラーを出してくれます。
では、どうすれば良いのでしょうか?
マジックナンバーを避けるためには、以下の方法が推奨されます。
-
意味のある変数名を使用する
計算結果などを変数に格納し、その変数を比較などに使用することで、数値の意味合いが間接的に伝わります。const remainingAttempts = maxAttempts - failedAttempts; if (remainingAttempts === 0) { // ... }
-
定数(const)を使用する
数値に意味のある名前をつけて定数として定義し、その定数を使用します。const MAX_USERS = 7; if (count > MAX_USERS) { // ... }
この例では、「7」という数値が
MAX_USERS
という定数名で表現されているため、コードの意図が明確になります。
「no-magic-numbers」ルールの設定例
.eslintrc.js
ファイルなどで、以下のように設定することでこのルールを有効にできます。
module.exports = {
// ...
rules: {
'no-magic-numbers': ['error', {
ignore: [-1, 0, 1, 2], // 除外したい数値を指定できます
ignoreArrayIndexes: true, // 配列のインデックスを無視するかどうか
enforceConst: true, // 定数での宣言を強制するかどうか
detectObjects: false, // オブジェクトのリテラル内の数値を検出するかどうか
}],
},
// ...
};
このように設定することで、「ESLint」はコード中のマジックナンバーを検出し、より可読性が高く保守しやすいコードを書く手助けをしてくれます。
一般的なエラー
-
-
現象
明らかに意図のある数値(例えば、初期値の0
やループの増減値1
など)に対しても、「no-magic-numbers」が警告やエラーを表示する。 -
原因
デフォルト設定では、特定の数値を除外する設定になっていないため、あらゆる数値がマジックナンバーとして認識される可能性があります。 -
解決策
.eslintrc.js
ファイルでignore
オプションを設定し、警告/エラーを無視したい数値を指定します。// .eslintrc.js module.exports = { rules: { 'no-magic-numbers': ['warn', { // エラーではなく警告にする例 ignore: [-1, 0, 1, 2, 10, 100], // よく使う数値を無視する }], }, };
-
-
配列のインデックスへの警告/エラー
-
現象
配列の要素にアクセスする際のインデックス(arr[0]
の0
など)に対して、「no-magic-numbers」が警告やエラーを表示する。 -
原因
配列のインデックスは文脈上その意味が明確であることが多いため、不要な警告となることがあります。 -
解決策
.eslintrc.js
ファイルでignoreArrayIndexes
オプションをtrue
に設定します。// .eslintrc.js module.exports = { rules: { 'no-magic-numbers': ['error', { ignoreArrayIndexes: true, }], }, };
-
-
オブジェクトのリテラル内の数値への警告/エラー
-
現象
オブジェクトのプロパティの値として記述された数値({ status: 1 }
の1
など)に対して、「no-magic-numbers」が警告やエラーを表示する。 -
原因
オブジェクトのプロパティ名が数値の意味を補完している場合でも、デフォルトではマジックナンバーとして認識されることがあります。 -
解決策
.eslintrc.js
ファイルでdetectObjects
オプションをfalse
に設定することで、オブジェクトリテラル内の数値の検出を無効化できます。ただし、この設定は他のオブジェクト内の数値も無視するため、注意が必要です。より細かく制御したい場合は、個別に定数を定義することを検討してください。// .eslintrc.js module.exports = { rules: { 'no-magic-numbers': ['error', { detectObjects: false, }], }, };
-
-
定数としての宣言忘れ
-
現象
マジックナンバーを定数に置き換えるべき箇所で、うっかり変数を宣言してしまい、「no-magic-numbers」が依然として警告/エラーを表示する。 -
原因
「no-magic-numbers」ルールは、デフォルトではconst
で宣言された定数の使用を推奨します。 -
解決策
問題の数値をconst
を用いて定数として宣言するように修正します。.eslintrc.js
でenforceConst: true
を設定している場合は、変数の使用がエラーとなります。// 修正前 let MAX_VALUE = 100; if (value > MAX_VALUE) { ... } // 修正後 const MAX_VALUE = 100; if (value > MAX_VALUE) { ... }
-
一般的なトラブルシューティング
-
設定ファイルの確認
まず、.eslintrc.js
ファイルの設定内容を再度確認し、「no-magic-numbers」ルールがどのように設定されているか(エラーレベル、ignore
オプションなど)を確認します。 -
ESLint の再実行
設定ファイルを変更した場合は、ESLint を再度実行して変更が反映されているか確認します。ターミナルでeslint .
やnpm run lint
などのコマンドを実行します。 -
特定の行を無視する
どうしてもマジックナンバーの使用が避けられない、または正当な理由がある場合は、特定の行に対して ESLint のルールを一時的に無効化するコメントを使用できます。ただし、多用は避けるべきです。// eslint-disable-next-line no-magic-numbers if (data[0] === 123) { // ... }
-
チームでの設定統一
チームで開発している場合は、「no-magic-numbers」ルールの設定についてチーム内で合意し、設定ファイルを共有することが重要です。 -
段階的な導入
大規模なプロジェクトに「no-magic-numbers」ルールを導入する場合、最初は警告レベルで導入し、徐々に修正していくことを検討すると、スムーズに進められます。 -
ルールの理解
「no-magic-numbers」ルールが何を意図しているのか、なぜマジックナンバーが問題なのかを理解することで、より適切な対応ができるようになります。
マジックナンバーの例(ESLint が警告/エラーを出す可能性のあるコード)
function calculateArea(radius) {
return 3.14 * radius * radius; // 3.14 はマジックナンバー
}
if (user.age > 18) { // 18 はマジックナンバー
console.log('成人です');
}
for (let i = 0; i < 10; i++) { // 10 はマジックナンバー
// 何らかの処理
}
const point = { x: 100, y: 200 }; // 100 と 200 はマジックナンバー
これらの例では、3.14
(円周率)、18
(成人年齢)、10
(ループの回数)、100
、200
(座標)といった数値が、その文脈だけでは意味が明確ではありません。これがマジックナンバーと呼ばれるものです。
定数を使用した改善例(ESLint の警告/エラーを回避するコード)
const PI = 3.14;
function calculateArea(radius) {
return PI * radius * radius; // 定数 PI を使用
}
const ADULT_AGE = 18;
if (user.age > ADULT_AGE) { // 定数 ADULT_AGE を使用
console.log('成人です');
}
const MAX_ITERATIONS = 10;
for (let i = 0; i < MAX_ITERATIONS; i++) { // 定数 MAX_ITERATIONS を使用
// 何らかの処理
}
const INITIAL_X = 100;
const INITIAL_Y = 200;
const point = { x: INITIAL_X, y: INITIAL_Y }; // 定数 INITIAL_X, INITIAL_Y を使用
このように、意味のある名前を持つ定数を使用することで、コードの可読性が向上し、数値の意味が明確になります。ESLint の「no-magic-numbers」ルールを有効にしている場合、最初の例は警告またはエラーになりますが、こちらの例は問題ありません。
列挙型(enum)を使用した改善例(複数の関連する定数を管理する場合)
const Status = {
PENDING: 0,
PROCESSING: 1,
COMPLETED: 2,
FAILED: 3,
};
function processTask(taskStatus) {
if (taskStatus === Status.COMPLETED) {
console.log('タスクが完了しました。');
} else if (taskStatus === Status.FAILED) {
console.log('タスクが失敗しました。');
}
}
processTask(Status.PROCESSING);
ここでは、タスクの状態を表す数値を Status
というオブジェクトのプロパティとして定義しています。これにより、数値の意味がより明確になり、コードも読みやすくなります。
意味のある変数名を使用した改善例(計算結果など)
const MAX_ATTEMPTS = 3;
let failedAttempts = 0;
function retry() {
failedAttempts++;
if (failedAttempts < MAX_ATTEMPTS) { // 定数 MAX_ATTEMPTS を使用
console.log('リトライします...');
// リトライ処理
} else {
console.log('リトライ回数が上限に達しました。');
}
}
const DISCOUNT_RATE = 0.1;
let price = 1000;
const discountedPrice = price * (1 - DISCOUNT_RATE); // 定数 DISCOUNT_RATE を使用
console.log(`割引後の価格: ${discountedPrice}`);
この例では、MAX_ATTEMPTS
や DISCOUNT_RATE
といった定数を使用することで、数値の意味が明確になっています。また、計算結果を変数 discountedPrice
に格納することで、1 - 0.1
という直接的な数値の使用を避けています。
例外的なケース(ESLint の設定で無視する場合)
.eslintrc.js
ファイルで以下のように設定することで、特定の数値を無視したり、配列のインデックスを無視したりできます。
// .eslintrc.js
module.exports = {
rules: {
'no-magic-numbers': ['warn', {
ignore: [-1, 0, 1, 2], // -1, 0, 1, 2 は警告を無視する
ignoreArrayIndexes: true, // 配列のインデックスは無視する
}],
},
};
const array = [10, 20, 30];
console.log(array[0]); // ignoreArrayIndexes: true のため警告は出ない
if (value === 0) { // ignore: [0] のため警告は出ない
// ...
}
設定ファイルでの例外定義
-
注意点
広範囲に無視設定をすると、本来検出されるべきマジックナンバーも見逃す可能性があります。 -
利点
コードの変更を最小限に抑えられます。 -
例
// .eslintrc.js module.exports = { rules: { 'no-magic-numbers': ['warn', { ignore: [-1, 0, 1, 2], // よく使う数値を無視リストに追加 ignoreArrayIndexes: true, // 配列のインデックスを無視 }], }, };
-
説明
コード自体を変更するのではなく、ESLint の設定ファイル (.eslintrc.js
) で特定の数値をマジックナンバーとして扱わないように例外定義する方法です。頻繁に使用される0
、1
、-1
など、文脈から意味が明らかな数値を無視するように設定できます。
意味のある変数名の活用 (直接的な代替ではないですが、可読性向上に貢献)
-
注意点
これはマジックナンバーそのものをなくすわけではありませんが、コードの理解を助けます。 -
利点
数値の背景にある計算や関連性が明確になります。 -
例
const baseValue = 10; const offset = 5; const threshold = baseValue + offset; // 直接的な数値 15 を使用しない if (inputValue > threshold) { // ... }
-
説明
数値を直接定数化する代わりに、その数値が計算された結果を変数に格納し、その変数を使用することで、コードの意図を間接的に示す方法です。
設定オブジェクトの利用
-
注意点
型安全性が列挙型ほど高くありません。 -
例
const UIConfig = { fontSizeSmall: 12, fontSizeMedium: 16, fontSizeLarge: 20, defaultTextColor: '#333', }; document.body.style.fontSize = `${UIConfig.fontSizeMedium}px`; document.body.style.color = UIConfig.defaultTextColor;
-
説明
複数の関連する数値をオブジェクトのプロパティとして定義し、意味のあるキー名でアクセスする方法です。列挙型と似ていますが、より柔軟に値を管理できます。
ユーティリティ関数の利用
-
注意点
関数の定義と呼び出しのオーバーヘッドが発生する可能性があります。 -
利点
数値の意味を関数名で表現でき、再利用性も高まります。 -
例
function getStatusCode(statusName) { switch (statusName) { case 'success': return 200; case 'error': return 500; case 'notFound': return 404; default: return -1; } } if (getResponseCode('success') === 200) { console.log('処理は成功しました。'); }
-
説明
特定の数値を生成したり、特定の意味を持つ数値を返したりするユーティリティ関数を作成する方法です。
ライブラリやフレームワークが提供する定数の利用
-
注意点
使用するライブラリやフレームワークに依存します。 -
利点
標準化された定数を利用できるため、一貫性が保たれ、可読性も向上します。 -
例
(Node.js の HTTP モジュールの場合)const http = require('http'); if (response.statusCode === http.StatusCodes.OK) { console.log('リクエストは成功しました。'); } else if (response.statusCode === http.StatusCodes.NOT_FOUND) { console.log('リソースが見つかりませんでした。'); }
-
説明
使用しているライブラリやフレームワークが、特定の値(例えば、HTTP ステータスコード、イベントの種類など)に対して定義済みの定数を提供している場合、それらを積極的に利用します。
どの方法を選ぶべきか?
最適な方法は、状況やチームのコーディング規約によって異なります。
- 外部ライブラリの値
ライブラリが提供する定数を活用しましょう。 - 複雑なロジックに関連する値
ユーティリティ関数が有効な場合があります。 - 関連する複数の値
列挙型や設定オブジェクトが適しています。 - 意味が明確な固定値
定数を使用するのが最も一般的で推奨されます。 - 頻繁に使用される基本的な数値
設定ファイルでの例外定義が手軽です。