TypeScript+React用にESLintを導入する

## 前提
TypeScript+Reactで開発環境を構築済みであること
参照：[TypeScript+Reactでモダンな環境に入門してみる](https://qiita.com/Captain_Blue/items/2cf22f0bd66136d05ebf)

各種バージョン
macOS Catalina 10.15.7
VS Code 1.49.3
node.js 14.4.0
yarn 1.22.10
TypeScript 4.0.3
React 17.0.1

## ESLintの設定

### パッケージを最新化する

```
$ yarn upgrade-interactive --latest
```

### ESLintの設定ファイルのテンプレートを作成する

`yarn eslint --init`を実行する。
すると、いくつか質問が表示されるので、各質問に以下で答える。最後の質問のあとでエラーがでるが気にしなくていいみたい。

```
$ yarn 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 // これを選択

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

? Which framework does your project use? … 
❯ React // これを選択
  Vue.js
  None of these

? Does your project use TypeScript? › No / Yes // Yes

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

? How would you like to define a style for your project? … 
❯ Use a popular style guide // これを選択
  Answer questions about your style
  Inspect your JavaScript file(s)

? Which style guide do you want to follow? … 
❯ Airbnb: https://github.com/airbnb/javascript // これを選択
  Standard: https://github.com/standard/standard
  Google: https://github.com/google/eslint-config-google

? What format do you want your config file to be in? … 
❯ JavaScript // これを選択
  YAML
  JSON
Checking peerDependencies of eslint-config-airbnb@latest
Local ESLint installation not found.
The config that you've selected requires the following dependencies:

eslint-plugin-react@^7.20.0 @typescript-eslint/eslint-plugin@latest eslint-config-airbnb@latest eslint@^5.16.0 || ^6.8.0 || ^7.2.0 eslint-plugin-import@^2.21.2 eslint-plugin-jsx-a11y@^6.3.0 eslint-plugin-react-hooks@^4 || ^3 || ^2.3.0 || ^1.7.0 @typescript-eslint/parser@latest

? Would you like to install them now with npm? › No / Yes // yarnを使いたいのでNoを選択
```

### 拡張ルールセット、プラグインをインストールする

上記の最後の質問でNoを選択した場合は、`? What format do you want your config file to be in? … `に答えたあとに表示されるdependenciesをインストールする必要がある。

```
$ yarn add -D @typescript-eslint/eslint-plugin \
eslint-plugin-react eslint-config-airbnb \
eslint eslint-plugin-import \
eslint-plugin-jsx-a11y eslint-plugin-react-hooks \
@typescript-eslint/parser

$ typesync
$ yarn
```

### ESLintの共有設定とプラグインルールを調整

`eslintrc.js`を用途に合わせて編集する。
`tsconfig.eslint.json`ファイルを用意し、対象とするファイルを設定する。
`.eslintignore`ファイルを用意しチェック対象外にするファイルを設定する。

```js:eslintrc.js
module.exports = {
  env: {
    browser: true,
    es2021: true,
  },
  extends: [
    'plugin:react/recommended',
    // Airbnbが提供する共有設定。広く使われている
    'airbnb',
    // 各プラグイン推奨共有設定
    'airbnb/hooks',
    'plugin:import/errors',
    'plugin:import/warnings',
    'plugin:import/typescript',
    'plugin:@typescript-eslint/eslint-recommended',
    'plugin:@typescript-eslint/recommended',
    'plugin:@typescript-eslint/recommended-requiring-type-checking',
  ],
  parser: '@typescript-eslint/parser',
  parserOptions: {
    ecmaFeatures: {
      jsx: true,
    },
    ecmaVersion: 2020,
    project: './tsconfig.eslint.json', // プロジェクトに対するコンパイル設定ファイルのパス
    sourceType: 'module',
    tsconfigRootDir: __dirname,
  },
  plugins: [
      '@typescript-eslint',
      'import',
      'jsx-a11y',
      'react',
      'react-hooks',
  ],
  root: true, // 親ディレクトリの設定ファイルを読み込まないように設定
  rules: {
    "no-use-before-define": "off",
    "@typescript-eslint/no-use-before-define": ["error"],
    // should be rewritten as `['error', { allowAsStatement: true }]` in ESLint 7 or later
    // SEE: https://github.com/typescript-eslint/typescript-eslint/issues/1184
    'no-void': ['error', { allowAsStatement: true }],
    '@typescript-eslint/no-unused-vars': [
      'error',
      {
        'vars': 'all',
        'args': 'after-used',
        'argsIgnorePattern': '_',
        'ignoreRestSiblings': false,
        'varsIgnorePattern': '_',
      },
    ],
    'import/extensions': [
      'error',
      'ignorePackages',
      {
        js: 'never',
        jsx: 'never',
        ts: 'never',
        tsx: 'never',
      },
    ],
    'react/jsx-filename-extension': [
      'error',
      {
        extensions: ['.jsx', '.tsx'],
      },
    ],
  },
  overrides: [
    {
      'files': ['*.tsx'],
      'rules': {
        'react/prop-types': 'off',
      },
    },
  ],
  settings: {
    'import/resolver': {
      node: {
        paths: ['src'],
      },
    },
  },
};
```

```js:tsconfig.eslint.json
{
  "extends": "./tsconfig.json",
  "include": [
    "src/**/*.js",
    "src/**/*.jsx",
    "src/**/*.ts",
    "src/**/*.tsx"
  ],
  "exclude": [
    "node_modules"
  ]
}
```

```:.eslintignore
build/
public/
**/coverage/
**/node_modules/
**/*.min.js
*.config.js
```

### npm-scriptsで一括でlintするscriptを追記する

`yarn start`と同じようにlintを実行できるようにしたい。
`package.json`のscriptsに以下を追記する。

```json:package.json
  "scripts": {
    "lint": "eslint 'src/**/*.{js,jsx,ts,tsx}'",
    "lint:fix": "eslint --fix 'src/**/*.{js,jsx,ts,tsx}'",
    "postinstall": "typesync"
  },
```

以下のようにlintを実行できるようになる。
また、`postinstall`でパッケージをインストール後に自動で`typesync`を実行してくれる。

```
$ yarn lint // src配下のjs・tsファイルをまとめてチェック
$ yarn lint:fix // src配下のjs・tsファイルをまとめてチェックしてfix
```

## 追記

@turara さんよりVS CodeにESLintのExtension入れると便利だよとのアドバイスをいただき、
VS Code側の設定についても追記。（@turara さん、ありがとうございます！）

### VS CodeにESLintプラグインをインストールする

※Macの場合での説明となります。

・`⌘ + shift + p`でコマンドパレットを開き、「install Extensions」と入力し、「Extensions: install Extensions」を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63564/99a6a331-4fd8-6e00-18aa-6b4de77e9555.png)


・検索にて「ESLint」と入力し、一番上に出てきたものをインストール
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63564/82309387-d83e-14fc-8554-29ed82d5fe28.png)

・`⌘ + shift + p`でコマンドパレットを開き、「settings」と入力し、「Preferences: Open Settings (json)」を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63564/e6c190ab-7ed9-a8e2-2cec-403b13f33cef.png)


・jsonファイルに設定を追記する

```json:settings.json
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true,
  },
  "editor.formatOnSave": false,
  "eslint.enable": true,
```

各設定について

```json
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true,
  }
```
→ ファイル保存時に`eslint --fix`を実行

```json
"editor.formatOnSave": false,
```
→ ESLintを使いたいので、VS Codeのフォーマッタを無効にしている

```json
"eslint.enable": true,
```
→ ESLintのExtensionを有効にしている


## 参考
[りあクト！ TypeScriptで始めるつらくないReact開発 第3版【Ⅱ. React基礎編】](https://oukayuka.booth.pm/items/2368019)
['React' was used before it was defined](https://stackoverflow.com/questions/63818415/react-was-used-before-it-was-defined)