ESLint で有効化するルールのカスタマイズ

2023-04-14

はじめに

こんにちは。Belong で Frontend Engineer をやっている hatakoya です。
この記事ではフロントエンドの開発でよく使われる Linter である ESLint について、ルールのカスタマイズ方法といくつかのルールについて紹介したいと思います。

ESLint 構成ファイルの書き方

簡単に構成ファイルでルールをカスタマイズする方法について説明します。
ESLint の構成ファイルは .eslintrc.js です。 .js 以外でも書けますがそこはお好みです。

ルールをカスタマイズするときは主に以下のプロパティを設定することになります。

  • plugins
  • extends
  • rules
  • overrides
const config = {
  plugins: [],
  extends: [],
  rules: {},
  overrides: [],
}

module.exports = config

plugins

plugins には ESLint のプラグイン(npm パッケージ)を指定します。
ESLint がデフォルトで備えているルールだけでも結構な量がありますが、プラグインを追加することでそのプラグインが提供する独自のルールをさらに追加することができます。

例えばeslint-plugin-reactを入れる場合は以下のように書きます。このとき "eslint-plugin" は省略できます。

const config = {
  plugins: ['react'],
}

module.exports = config

extends

extends には Shareable Configs(npm パッケージ)を指定します。
これにより外部の ESLint 構成ファイルをベースに変えたいところだけ自分たちでカスタマイズするといったことができます。

基本的にはプラグイン自体が Shareable Configs も提供していて、そのプラグイン用の推奨構成などが用意されていたりします。
プラグインを入れることにより競合する ESLint ルールを無効にしたり、プラグインを動かすために必要な構成ファイルの settingsparserOption プロパティの設定値などもいい感じに設定してくれるので、それらを自分で書くよりは提供される構成を利用してしまうのが手っ取り早いです。

プラグインの Shareable Configs にはその構成自体に自身のプラグインを追加する構成も含まれていたりするので、その場合は plugins は設定しなくても良いです。

例:
https://github.com/jsx-eslint/eslint-plugin-react/blob/ef733fd420c821f7159b28f4a36ce5e09375f9b8/index.js#L126-L159

const config = {
  extends: ['plugin:react/recommended'],
}

module.exports = config

rules

rules では各ルールについて有効 or 無効の設定をします。
Shareable Configs の設定をベースに、変えたいものを設定していきます。 基本的には 'off''error' などを設定しますが、ルールによってはオプションがあり、オプションをデフォルトから変えたい場合は配列で設定します。

const config = {
  rules: {
    // 無効にする
    'no-undef': 'off',
    // 有効にする(警告)
    'no-undef': 'warn',
    // 有効にする(エラー)
    'no-undef': 'error',
    // 有効にする(オプションつき)
    'array-callback-return': [
      'error',
      { allowImplicit: false, checkForEach: true },
    ],
  },
}

module.exports = config

overrides

特定のディレクトリ内のファイルだけルールを別にしたい場合は overrides を設定します。
これは例えば eslint-plugin-import で基本的に default export を禁止したいが、Next.js の pages ディレクトリのように default export しなければならないファイルについてはルールを変えたいといったときに使えます。

const config = {
  rules: {
    // Default export を禁止する
    'import/no-default-export': 'error',
    'import/prefer-default-export': 'off',
  },
  overrides: [
    {
      files: ['pages/**/*.{ts,tsx}'],
      rules: {
        // Default export を強制する
        'import/no-default-export': 'off',
        'import/prefer-default-export': 'error',
      },
    },
  ],
}

module.exports = config

ESLint のルールについて

JavaScript や TypeScript のリンターとして ESLint を利用するにあたり、有効化するルールは eslint:recommended などの推奨ルールを設定したり、 eslint-config-airbnb のようなものを使うと楽です。
ただし、ルールを厳しくしたり緩くしたり、チームに合った適切な設定にするためには各ルールのドキュメントを読み、内容をある程度理解する必要がでてきます。

ESLint にはたくさんのルールがありますが、この中から eslint:recommended に含まれないものをいくつか抜粋して紹介したいと思います。

array-callback-return

配列の mapfind などのメソッドで return することを強制するルールです。 コーディングスタイルを揃えたい場合は有効にすると良いです。

オプション:

  • allowImplicit(default: false) - 値なしの returnundefined を返すことを許可するか
  • checkForEach(default: false) - forEach で値つきの return を許可するか

checkForEachtrue にするのが良いと思います。

'array-callback-return': [
  'error',
  { allowImplicit: false, checkForEach: true },
],

no-constant-binary-expression

常に true or false になる比較式や、常に通らない ||, &&, ?? を禁止します。

no-implicit-coercion

!!foo の代わりに Boolean(foo) を使うようにするなど、わかりにくそうな型変換を禁止します。

no-else-return

不要な else を禁止する。
allowElseIf: false にすると、if の中で return する場合は else-if を禁止します。

'no-else-return': ['error', { allowElseIf: false }],

no-unneeded-ternary

条件が常に true または false になる三項演算子を禁止します。

no-useless-return

不要な return を禁止します。

object-shorthand

オブジェクトのプロパティの値が変数と同じ名前の場合は省略記法を強制します。

// NG
const foo = {
  a: a,
}

// OK
const foo = {
  a,
}

prefer-const

const で宣言できる変数は const で宣言するようにします。

arrow-body-style

アロー関数の中括弧を強制または禁止します。

1つ目のオプション

  • “always” - 常に中括弧を強制する
  • “as-needed” - 中括弧を省略できる場合は中括弧なしを強制する。
  • “never” - 中括弧なしを強制する(値を返さないアロー関数は書けなくなる)

2つ目のオプション

  • requireReturnForObjectLiteral (default: false) - “as-needed” の場合にのみ設定できる。true にするとオブジェクトリテラルを返す場合に return を強制する。
    • true にすると以下のようになります
// NG
const func = () => ({ a: 1 })

// OK
const func = () => {
  return { a: 1 }
}

consistent-return

関数内の複数の return がある場合に値を指定する/しないが一貫することを強制します。

オプション:

  • treatUndefinedAsUnspecified
    • true - 値なしは return undefined でもreturnでも OK
    • false (default) - 値なしはreturn undefinedではなくreturnを強制
'consistent-return': ['error', {
    treatUndefinedAsUnspecified: false,
  }],

curly

if 文などで中括弧{}を強制します。

default-case

switch-case で default を強制します。

default-case-last

switch-case で default を最後に書くことを強制します。

eqeqeq

==!= の代わりに ===!== を使うようにします。
オプションで、 null との比較をする場合は ==!= しか使えないようにすることもできます。

eqeqeq: ["error", "always", { null: "never" }],

no-duplicate-imports

同じモジュールからの import は 1 行にまとめることを強制します。

// NG
import { foo } from 'myModule'
import { bar } from 'myModule'

// OK
import { foo, bar } from 'myModule'

TypeScript の import type を使う場合は代わりに import/no-duplicates を使います。

おわりに

ESLint のルールを整備することで、コーディングスタイルを統一したり、バグを防ぐことができます。
上記で紹介したルールは一例ですが、recommended で有効にならないルールの中でも有用なものも多いので、必要に応じて追加していくのが良いと思います。 もしくは eslint:all を有効にして、必要なルールだけ無効にするという方法もあります。その場合は無効または変更するルールが rules 内に明示的に書かれるので、どのルールをカスタマイズしているかがわかりやすいかもしれません。

Belong のフロントエンドは TypeScript/Next.js で開発しており、各種プラグインを入れて ESLint を活用しています。 機会があれば、他のプラグインなどについても紹介したいと思います。

また、弊社 Belong は一緒に働くエンジニアを募集しています。
興味がある方は https://entrancebook.belonginc.dev/ をご覧いただけると幸いです。