JavaScriptのコードを書く際、ESLintによって静的解析をするのはデファクトスタンダードであると言ってもいいでしょう。
ESLintとともにeslint-config-standardやeslint-config-airbnbなどのShareable Configsを使うことが多いかと思いますが、それらにくわえてeslint-plugin-unicornも併用すると、コード品質のさらなる向上が期待できます。
導入方法
eslint-plugin-unicorn
をインストールします。
npm i -D eslint-plugin-unicorn
yarn add -D eslint-plugin-unicorn
そして、ESLintの設定のextends
にplugin:unicorn/recommended
を追加すると、eslint-plugin-unicornの推奨設定が適用されます。
{
"extends": [
"plugin:unicorn/recommended"
]
}
推奨設定の中には「これはどうなんだ」というものもあるので、プロジェクトに応じてよしなにカスタマイズするといいでしょう。
たとえば、略語の使用を禁止するprevent-abbreviations
というルールを無効化したい場合、次のようにします。
{
"extends": [
"plugin:unicorn/recommended"
],
"rules": {
"unicorn/prevent-abbreviations": "off"
}
}
私は自分用のShareable Configをつくり、その中でいくつかのルールを上書きして使っています。
イチオシのルール
100を超えるeslint-plugin-unicornのルールの中から、特に私がイチオシしたいものをピックアップしてご紹介します。
better-regex
正規表現の記法を最適化します。
たとえば、[0-9]
は\d
になります。
/^[0-9]+$/.test('123')
/^\d+$/.test('123')
catch-error-name
キャッチされたエラーの変数名をerror
などに強制します。
try {
await fetch('https://example.com')
} catch (e) {
console.error(e)
}
try {
await fetch('https://example.com')
} catch (err) {
console.error(err)
}
try {
await fetch('https://example.com')
} catch (error) {
console.error(error)
}
// fooError のような形式も許容される
try {
await fetch('https://example.com')
} catch (fetchError) {
console.error(fetchError)
}
consistent-function-scoping
関数の定義される場所が、なるべく上位のスコープになるようにします。
const findFooElement = () => {
// isFooElement は findFooElement の外側に移動できる
const isFooElement = (element) => {
return element.textContent.includes('foo')
}
return Array.from(document.querySelectorAll('*')).find(element => isFooElement(element))
}
const isFooElement = (element) => {
return element.textContent.includes('foo')
}
const findFooElement = () => {
return Array.from(document.querySelectorAll('*')).find(element => isFooElement(element))
}
expiring-todo-comments
TODOコメントに日付やバージョンなどの条件を併記することで、その条件が満たされたらエラー扱いにすることができます。
// TODO [2022-12-31]: 2022年中に実装する
// TODO [>=1.0.0]: v1のリリースまでに実装する
// TODO [engine:node@>=18.0.0]: top level awaitに置き換える
// TODO [+date-fns]: date-fnsに置き換える
// TODO [-jquery]: ネイティブのAPIに置き換える
// TODO [uuid@>=8.0.0]: named importに置き換える
no-abusive-eslint-disable
ESLintの無効化コメントを記載する際、ルールの指定を強制します。
ルール単位で無効化することで、本来であれば検出されるはずのエラーが見過ごされてしまうような事態を防げます。
// eslint-disable-next-line
console.log('Hello World')
// eslint-disable-next-line no-console
console.log('Hello World')
no-array-callback-reference
Array.map()
などの高階関数にコールバック関数を直接渡すことを禁止します。
これにより、以下のように2個以上の引数をとる関数を渡したときの予期せぬ挙動を回避することができます。
['10', '10', '10'].map(Number.parseInt) // [10, NaN, 2]
['10', '10', '10'].map(number => Number.parseInt(number)) // [10, 10, 10]
ただし、TypeScriptにおいて型ガードを適用するためには型ガード関数を直接渡す必要があるので、その際はコメントで無効化するとよいでしょう。
import { isNotUndefined } from 'type-guards'
const nullableValues: Array<number | undefined> = [1, 2, undefined]
const values1 = nullableValues.filter(value => isNotUndefined(value))
// values1 の型は (number | undefined)[]
// eslint-disable-next-line unicorn/no-array-callback-reference
const values2 = nullableValues.filter(isNotUndefined)
// values2 の型は number[]
no-negated-condition
条件式が否定形にならないようにします。
if (!isFoo) {
doBar()
} else {
doFoo()
}
if (isFoo) {
doFoo()
} else {
doBar()
}
no-null
null
の使用を禁止します。
値がないことを表現したいときは、常にundefined
を使います。
const foo = null
const foo = undefined
特にTypeScriptにおいては、null
ではなくundefined
を使うのは理に適っています。
prefer-modern-dom-apis
DOM操作について、よりモダンなAPIの使用を優先します。
foo.insertAdjacentElement('beforebegin', bar)
foo.insertAdjacentElement('afterbegin', bar)
foo.insertAdjacentElement('beforeend', bar)
foo.insertAdjacentElement('afterend', bar)
foo.before(bar)
foo.prepend(bar)
foo.append(bar)
foo.after(bar)
prefer-node-protocol
Node.jsのビルトインモジュールをインポートする際、node:
プロトコルの記載を強制します。
これにより、インポートされているのがビルトインモジュールなのか否かが明確になります。
import fs from 'fs'
import fs from 'node:fs'
node:
プロトコルを記載することのメリットについては、以下の記事が詳しいです。
prefer-number-properties
isNaN()
などのグローバル空間に生えている関数よりも、Number.isNaN()
などのNumber
に生えている関数の使用を優先します。
isNaN(123)
Number.isNaN(123)
おわりに
以前はコードレビューで指摘したりされたりしていたようなことも、eslint-plugin-unicornの導入後はESLintによってコーディング段階で検出されるようになりました。
機械的に判定可能なものの指摘はリンターに任せることで、我々人間はロジックなど本質的な部分に注力することができます。
みなさんもぜひ、eslint-plugin-unicornを使ってみてください。