JavaScriptの基数変換を明確にする:ESLint radixルールと代替方法
ESLint の radix ルールは、JavaScript の parseInt()
関数を使用する際に、意図しない基数での数値変換を防ぎ、コードの信頼性と保守性を向上させるためのものです。
問題点
parseInt()
関数は、文字列を数値に変換する際に、オプションで基数 (radix) を指定することができます。しかし、多くの場合、この基数が省略されてしまいます。
デフォルトでは、parseInt()
関数は文字列の先頭文字に基づいて基数を自動的に判別します。具体的には、
- それ以外の場合は 10 進数 (decimal)
- 先頭に
0
がある場合は 8 進数 (octal) - 先頭に
0x
がある場合は 16 進数 (hexadecimal)
として解釈されます。
しかし、この自動判別は、意図しない結果を招く可能性があります。例えば、以下のコードを見てみましょう。
var num = parseInt("071");
console.log(num); // 出力: 57
このコードでは、文字列 "071"
を数値に変換しています。しかし、基数が省略されているため、parseInt()
関数は文字列の先頭の 0
を考慮して 8 進数として解釈し、結果として 57
という値が返されます。
一方、以下のコードでは、基数を明示的に指定することで、意図した結果を得ることができます。
var num = parseInt("071", 10);
console.log(num); // 出力: 71
このコードでは、文字列 "071"
を 10 進数として解釈し、結果として 71
という値が返されます。
radix ルールの役割
radix ルールは、以下の2つのオプションを提供することで、parseInt()
関数における基数の扱い方を制御します。
- "as-needed": 10 進数以外の基数を意図している場合のみ、基数の明示を要求します。
- "always" (デフォルト): 常に基数を明示的に指定することを強制します。
"always" オプションの使用例
/*eslint radix: "error"*/
var num = parseInt("071"); // エラー: 基数が明示的に指定されていません
var num = parseInt(someValue); // エラー: 基数が明示的に指定されていません
var num = parseInt("071", 10); // 正しい
"as-needed" オプションの使用例
/*eslint radix: ["error", "as-needed"]*/
var num = parseInt("071"); // 正しい: 10 進数と解釈される
var num = parseInt(someValue); // エラー: 基数が明示的に指定されていません
var num = parseInt("071", 16); // 正しい: 16 進数と解釈される
利点
radix ルールを使用することで、以下の利点が得られます。
- コードの保守性が向上します。
- コードのバグを発見しやすくなります。
- コードの意図が明確になり、誤解を招きにくくなります。
- コードレビューの際に、意図しない基数での変換を見逃さないようにしたい場合
- 古い JavaScript エンジンを使用する環境
- 異なる基数の数値を扱うコード
"always" オプション
var num1 = parseInt("071");
var num2 = parseInt(someString);
修正コード
var num1 = parseInt("071", 10); // 正しい
var num2 = parseInt(someString, 10); // 正しい
正しいコード
var num1 = parseInt("071"); // 正しい: 10 進数と解釈される
var num2 = parseInt(someString, 16); // 正しい: 16 進数と解釈される
エラーが発生するコード
var num3 = parseInt(someOtherString); // エラー: 基数が明示的に指定されていません
修正コード
var num3 = parseInt(someOtherString, 10); // 正しい
"as-needed"
オプションの場合、10 進数以外の基数を意図している場合のみ、基数の明示を要求します。"always"
オプションの場合、すべてのparseInt()
呼び出しにおいて、基数を明示的に指定する必要があります。- 上記の例では、
"always"
オプションと"as-needed"
オプションのそれぞれで、エラーが発生するコードと修正コードを示しています。
- コードレビューの際には、radix ルールを使用して、意図しない基数での変換を見逃さないように注意することが重要です。
- 実際のコードでは、状況に応じて適切なオプションを選択してください。
文字列の書式を明確にする
基数によって異なる書式を使用することで、基数の違いを明示することができます。
- 2 進数
0b
プレフィックスを使用 (例:0b101011
) - 8 進数
0
プレフィックスを使用 (例:077
) - 16 進数
0x
プレフィックスを使用 (例:0xAF
) - 10 進数
通常の数字表現
例
var num1 = parseInt("71"); // 10 進数と解釈される
var num2 = parseInt("0xAF"); // 16 進数と解釈される
var num3 = parseInt("077"); // 8 進数と解釈される
var num4 = parseInt("0b101011"); // 2 進数と解釈される
利点
- radix ルールを使用する必要がない。
- コードがより読みやすくなり、意図が明確になる。
欠点
- 一部のライブラリや API では、書式が異なる場合があります。
- すべての基数に対して書式を統一する必要がある。
基数を明示的に引数として渡す
parseInt()
関数の第二引数に、基数を数値として渡すことができます。
var num1 = parseInt("71", 10);
var num2 = parseInt("AF", 16);
var num3 = parseInt("77", 8);
var num4 = parseInt("101011", 2);
利点
- 任意の基数での数値変換が可能。
- コードが明確で分かりやすい。
欠点
- 一部のライブラリや API では、引数の順序が異なる場合があります。
- 冗長な記述になる。
代替の関数を使用する
parseInt()
関数以外にも、基数変換を行う関数があります。例えば、以下のような関数があります。
- lodash.parseInt(): Lodash ライブラリに含まれる関数。
parseInt()
関数とほぼ同じ機能だが、オプションでデフォルトの基数を指定できる。 - Number.parseInt(): ECMAScript 6 で導入された関数。
parseInt()
関数とほぼ同じ機能だが、オプションでグローバルな基数設定を無効にすることができる。
例 (lodash.parseInt() を使用)
var _ = require('lodash');
var num1 = _.parseInt("71");
var num2 = _.parseInt("AF", 16);
var num3 = _.parseInt("77", 8);
var num4 = _.parseInt("101011", 2);
利点
- 独自のオプションや機能を提供している場合がある。
- コードが簡潔になる場合がある。
欠点
- すべての環境で利用できるわけではない。
- 外部ライブラリの導入が必要。
どの方法を選択するべきか
どの方法を選択するかは、状況によって異なります。
- ライブラリの機能を活用したい場合は、代替の関数を使用する 方法が適しています。
- 柔軟性と汎用性を重視する場合は、基数を明示的に引数として渡す 方法が適しています。
- コードの可読性と簡潔性を重視する場合は、文字列の書式を明確にする 方法が適しています。
- 対象となる環境やライブラリ
- チームのコーディング規約
- コードの既存のスタイル