[React] ESLint

2023-01-25 hit count image

Reactでソースコードを分析してバグやエラーを探すため、ESLintを使う方法について説明します。

ブログシリーズ

このブログはシリーズで作成されております。次はブログシリーズのリストです。

概要

ESLintとはES(EcmaScript) + Lint(エラーコード表示)の合成語でJavascriptのコードを分析して、潜在的なエラーやバグを見つけるに役に立つツールです。

このブログポストではcreate-react-appで生成したReactプロジェクトでESLintを適用する方法について説明します。

プロジェクトの準備

ReactでESLintを使うため、create-react-appでプロジェクトを生成してみます。create-react-appに詳しくない方は、下記のブログポストを確認してみてください。

そしたら、次のコマンドを実行してESLintを実行するReactプロジェクトを生成します。

npx create-react-app eslint_example --template=typescript

私は主にTypeScriptを使ってReactを開発してるので--template=typescriptオプションを使ってReactプロジェクトを生成しました。

ESLintのインストール

ReactでESLintを使うためにはESLintのライブラリをインストールする必要があります。次のコマンドを使ってESLintのライブラリをインストールします。

# cd eslint_example
npm install eslint --save-dev

ESLintの設定

ReactでESLintを使うためにはESLintを設定する必要があります。次のコマンドを実行してESLintを設定します。

npx eslint --init

上のコマンドを実行すると、次のような質問が表示されます。

? How would you like to use ESLint? …
  To check syntax only
❯ To check syntax and find problems
  To check syntax, find problems, and enforce code style

自分が使いたいオプションを選択します。私はTo check syntax and find problemsを選択しました。そしたら、下記のような質問が表示されます。

? What type of modules does your project use? …
❯ JavaScript modules (import/export)
  CommonJS (require/exports)
  None of these

Reactは基本的import/exportの方法を使ってるので、JavaScript modules (import/export)を選択します。そしたら、下記のように質問が表示されます。

? Which framework does your project use? …
❯ React
  Vue.js
  None of these

私たちはESLintをReactプロジェクトで使う予定なので、Reactを選択して進めます。そしたら、下記のような質問が表示されます。

? Does your project use TypeScript? › No / Yes

私はReactを開発する時、主にTypeScriptを使ってるのでYesを選択して進めました。TypeScriptを使ってない方はNoを選択して進めてください。そしたら、下記のような質問が表示されます。

? Where does your code run? …  (Press <space> to select, <a> to toggle all, <i> to invert selection)
✔ Browser
✔ Node

ReactはBrowserでコードが実行されるので、Browserを選択して進めます。そしたら、下記のような質問が表示されます。

? What format do you want your config file to be in? …
❯ JavaScript
  YAML
  JSON

今まで返信したESLintの設定の内容を保存するファイルのフォーマットを選択するオプションです。私はJavaScriptフォーマットを好むので、JavaScriptを選択しました。

eslint-plugin-react@latest @typescript-eslint/eslint-plugin@latest @typescript-eslint/parser@latest
? Would you like to install them now with npm? › No / Yes

最後に、このように設定した内容に必要なライブラリをインストールするかどうか決めるオプションが表示されます。Yesを選択して必要なライブラリをインストールします。

ESLintのルール

ESLintの公式サイトとTypeScript ESLintの公式ページで設定可能なルール(Rule)を確認することができます。

  • ESLintの公式サイト: Rules
  • ESLint Plugin TypeScript: Rules

当該ルールなどを確認して、自分のプロジェクトに合わせてルールを設定することができます。下記のルールは私が使ってるルールの内容です。

module.exports = {
  env: {
    browser: true,
    es2021: true,
  },
  extends: [
    'eslint:recommended',
    'plugin:react/recommended',
    'plugin:@typescript-eslint/recommended',
    'plugin:storybook/recommended',
  ],
  parser: '@typescript-eslint/parser',
  parserOptions: {
    ecmaFeatures: {
      jsx: true,
    },
    ecmaVersion: 12,
    sourceType: 'module',
    project: './tsconfig.json',
  },
  plugins: ['react', '@typescript-eslint', 'functional', 'import'],
  settings: {
    react: {
      version: 'detect',
    },
  },
  rules: {
    // General
    'no-console': ['error', { allow: ['debug', 'warn', 'error'] }],
    // TypeScript
    '@typescript-eslint/consistent-type-imports': 'error',
    '@typescript-eslint/explicit-function-return-type': 'off',
    '@typescript-eslint/explicit-member-accessibility': 'off',
    '@typescript-eslint/indent': 'off',
    '@typescript-eslint/member-delimiter-style': 'off',
    '@typescript-eslint/no-confusing-void-expression': [
      'error',
      {
        ignoreArrowShorthand: true,
        ignoreVoidOperator: true,
      },
    ],
    'no-duplicate-imports': 'off',
    '@typescript-eslint/no-duplicate-imports': 'error',
    '@typescript-eslint/no-implicit-any-catch': 'error',
    'no-invalid-this': 'off',
    '@typescript-eslint/no-invalid-this': 'error',
    '@typescript-eslint/no-invalid-void-type': 'error',
    'no-loop-func': 'off',
    '@typescript-eslint/no-loop-func': 'error',
    'no-loss-of-precision': 'off',
    '@typescript-eslint/no-loss-of-precision': 'error',
    '@typescript-eslint/no-parameter-properties': 'off',
    'no-redeclare': 'off',
    '@typescript-eslint/no-redeclare': 'error',
    'no-shadow': 'off',
    '@typescript-eslint/no-shadow': 'error',
    'no-throw-literal': 'off',
    '@typescript-eslint/no-throw-literal': 'error',
    '@typescript-eslint/no-unnecessary-boolean-literal-compare': 'error',
    '@typescript-eslint/no-unnecessary-condition': 'error',
    '@typescript-eslint/no-unnecessary-type-arguments': 'error',
    'no-unused-expressions': 'off',
    '@typescript-eslint/no-unused-expressions': 'error',
    '@typescript-eslint/no-unused-vars': 'error',
    '@typescript-eslint/no-use-before-define': [
      'error',
      {
        variables: false,
      },
    ],
    '@typescript-eslint/prefer-enum-initializers': 'error',
    '@typescript-eslint/prefer-for-of': 'error',
    '@typescript-eslint/prefer-includes': 'error',
    '@typescript-eslint/prefer-nullish-coalescing': 'error',
    '@typescript-eslint/prefer-optional-chain': 'error',
    '@typescript-eslint/prefer-reduce-type-parameter': 'error',
    '@typescript-eslint/prefer-string-starts-ends-with': 'error',
    '@typescript-eslint/prefer-ts-expect-error': 'error',
    '@typescript-eslint/promise-function-async': 'error',
    'no-return-await': 'off',
    '@typescript-eslint/return-await': 'error',
    '@typescript-eslint/strict-boolean-expressions': 'error',
    '@typescript-eslint/switch-exhaustiveness-check': 'error',
    // React
    'react/jsx-boolean-value': 'warn',
    'react/jsx-curly-brace-presence': 'warn',
    'react/jsx-fragments': 'warn',
    'react/jsx-no-useless-fragment': 'warn',
    'react/jsx-uses-react': 'off',
    'react/prefer-stateless-function': 'warn',
    'react/prop-types': 'off',
    'react/react-in-jsx-scope': 'off',
    // Functional
    'functional/prefer-readonly-type': [
      'error',
      {
        allowLocalMutation: true,
        allowMutableReturnType: true,
        ignoreClass: true,
      },
    ],
    'import/order': [
      'error',
      {
        groups: ['builtin', 'external', 'internal'],
        pathGroups: [
          {
            pattern: '{react,react-dom/**}',
            group: 'external',
            position: 'before',
          },
        ],
        pathGroupsExcludedImportTypes: ['react'],
        'newlines-between': 'always',
        alphabetize: {
          order: 'asc',
          caseInsensitive: true,
        },
      },
    ],
    'react/react-in-jsx-scope': 'off',
    'linebreak-style': ['error', 'unix'],
    eqeqeq: ['error', 'always', { null: 'ignore' }],
    camelcase: ['error', { properties: 'never' }],
    quotes: ['error', 'single', { avoidEscape: true }],
  },
};

上のルールを使うためには下記のコマンドを実行してeslint-plugin-functionalをインストールする必要があります。

npm install --save-dev eslint-plugin-functional

ESLintのチェック

下記のコマンドを使って私たちが定義したESLintのルールを守ってないファイルと内容を確認することができます。

npx eslint ./src

コマンドを実行すると次のように定義したルールを守ってないファイルと内容が確認できます。

/eslint_test/src/App.tsx
  5:1  warning  Missing return type on function  @typescript-eslint/explicit-module-boundary-types

/eslint_test/src/reportWebVitals.ts
  1:1   error    All imports in the declaration are only used as types. Use `import type`  @typescript-eslint/consistent-type-imports
  3:25  warning  Missing return type on function                                           @typescript-eslint/explicit-module-boundary-types

✖ 3 problems (1 error, 2 warnings)
  1 error and 0 warnings potentially fixable with the `--fix` option.

ESLintでコードを修正

次のコマンドを使うと私たちが定義したESLintのルールを使ってコードを修正することができます。

npx eslint --fix ./src

コマンドを実行するとESLintが修正できるファイルは修正して、修正できないファイルを次のように表示されます。

/eslint_test/src/App.tsx
  5:1  warning  Missing return type on function  @typescript-eslint/explicit-module-boundary-types

/eslint_test/src/reportWebVitals.ts
  3:25  warning  Missing return type on function  @typescript-eslint/explicit-module-boundary-types

修正できなかったファイルはファイルを開いて直接修正します。

// code ./src/App.tsx
function App(): JSX.Element {
// code ./src/reportWebVitals.tsx
const reportWebVitals = (onPerfEntry?: ReportHandler): void => {

このようにファイルを修正して、再びコマンドを実行してコードをチェックしてみると、以前とは違ってエラーが表示されないことが確認できます。

npx eslint ./src

これで私たちが事前に定義したESLintのルールをよく守ってることが分かります。

私はESLintがコードを自動で修正してくれる--fixのオプションをあまり使ってないです。直接コードを見て、理由を分かることも大事だっと思うことと、時々ESLintが間違って修正する場合もあるからです。

package.jsonの設定

上で見たcheckコマンドとfixコマンドをもっと簡単に使えるようにするためpackage.jsonファイルを開いて下記のように修正します。

"scripts": {
  ...
  "lint": "eslint ./src",
  "lint:fix": "eslint --fix ./src"
},

このようにpackage.jsonファイルを修正すると、次のコマンドを実行してESLintを使うことができます。

npm run lint
npm run lint:fix

完了

これでReactプロジェクトでESLintを設定する方法について見てみました。今からESLintを使って潜在的なエラーやバグを直してみてください。

私のブログが役に立ちましたか?下にコメントを残してください。それは私にとって大きな大きな力になります!

アプリ広報

今見てるブログを作成たDekuが開発したアプリを使ってみてください。
Dekuが開発したアプリはFlutterで開発されています。

興味がある方はアプリをダウンロードしてアプリを使ってくれると本当に助かります。

Posts