React × Typescript × ESLint × Prettier × VSCodeなSetup


初投稿から一年くらい経って、各種ライブラリも少し様変わりしたので書き直しました。
21spring verです。

こちらでメモしつつ、変更したのでよかったら参照ください。
https://zenn.dev/sh090/scraps/3a6fed549d4716

チェケラ

## はじめに

create react app with tsなプロジェクトにeslintとprettierを入れる記事です。

**やりたいこと** 

1. react × typescript なLint設定
2. Lintエラーの解消
3. VScode保存時に自動整形

3は別記事がたくさんありますが一応


**注意書きみたいなの**

- node, yarnがinstallされてる前提で話進めます
- yarn 使います
- [Create React App](https://create-react-app.dev/) 使います
- style guide はAirbnb
- ルールは各プラグインの推奨設定 ＋ α

#####  各種version

| packages         | Version |
| ---------------- | ------- |
| react            | 17.0.2  |
| react-scripts    | 4.0.3   |
| eslint           | 7.20.0  |
| typescript       | 4.1.2   |


## 1. プロジェクト作成 :hatching_chick:

プロジェクト作る。

```bash
yarn create react-app react-app --template typescript
```

`eslint --init`   ここでairbnbを選択します。

```bash
yarn run eslint --init
```
```shell:回答内容
✔ How would you like to use ESLint? · style
✔ What type of modules does your project use? · esm
✔ Which framework does your project use? · react
✔ Does your project use TypeScript? · No / Yes
✔ Where does your code run? · browser
✔ How would you like to define a style for your project? · guide
✔ Which style guide do you want to follow? · airbnb
✔ What format do you want your config file to be in? · JavaScript
```

## 2. package入れてく  :package: 

`eslint --init` の質問で npm でインストールしないって選択したので、手動インストールする必要がある。

また prettier 関連のパッケージもインストールします。

### airbnb
[eslint-config-airbnb - npm](https://www.npmjs.com/package/eslint-config-airbnb)
以下のコマンド で依存関係のパッケージも良い感じにインストールしてくれる

```bash
$ npx install-peerdeps --dev eslint-config-airbnb
```
```:質問
It seems as if you are using Yarn. Would you like to use Yarn for the installation?
```
Yarn使ってるんで `y`

### typescript-eslint
[@typescript-eslint/eslint-plugin - npm](https://www.npmjs.com/package/@typescript-eslint/eslint-plugin)

```bash 
$ yarn add -D @typescript-eslint/eslint-plugin @typescript-eslint/parser
```

### prettier 
[prettier/eslint-config-prettier](https://github.com/prettier/eslint-config-prettier#readme)

```bash 
$ yarn add -D prettier eslint-config-prettier
```

## 3. 設定 :gear:

### .eslintrc.js

`extends` 追記

```diff_javascript:.eslintrc.js
extends: [
    'plugin:react/recommended',
    'airbnb',
+   'airbnb/hooks',
+   'plugin:@typescript-eslint/recommended',
+   'plugin:@typescript-eslint/recommended-requiring-type-checking',
+   'prettier',
],
```

`parserOptions` 追記

```diff_javascript:eslintrc.js
  parserOptions: {
    ecmaFeatures: {
      jsx: true,
    },
    ecmaVersion: 12,
    sourceType: 'module',
+   tsconfigRootDir: __dirname,
+   project: ['./tsconfig.json'],
  },
```

### .prettierrc [:link:](https://prettier.io/docs/en/configuration.html)
作ります。
[Options一覧](https://prettier.io/docs/en/options.html#trailing-commas)

```json:.prettierrc
{
    "singleQuote": true
}
```

衝突ルール検出

```bash
$npx eslint-config-prettier 'src/**/*.{js,jsx,ts,tsx}'
No rules that are unnecessary or conflict with Prettier were found.
```


### package.json
`package.json` に script 追記。 eslintConfigを除去。

```diff_json:package.json
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
+   "lint": "eslint --ext .ts,.tsx ./src",
+   "fix": "yarn format && yarn lint:fix",
+   "format": "prettier --write 'src/**/*.{js,jsx,ts,tsx}'",
+   "lint:fix": "eslint --fix 'src/**/*.{js,jsx,ts,tsx}'",
    "eject": "react-scripts eject"
  },
- "eslintConfig": {
-   "extends": "react-app"
- },
```

### .eslintignore
作ります

```:.eslintignore
build/
public/
**/node_modules/
*.config.js
.*lintrc.js
```

vscode で `.eslintignore` 無しで src配下だけ eslintで見てほしいときの書き方がわからなかったので、 知ってる方いたら教えて下さい。
まぁ置いといて安定な気もする。

## 4. Lintエラー対応 :syringe:
`yarn lint` してみる。まぁエラーでる。
ソース修正、ルールを追記していく。

### rule を追記する

とりあえずLintエラー解消のための最小設定

#### import/resolver , import/extensions [:link:](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/extensions.md)

拡張子が `ts` or `tsx` なファイルのインポートが解釈されない。
`rules` および `settings`に追記する。

```diff_javascript:eslintrc.js
  rules: {
+   'import/extensions': [
+     'error',
+     {
+       js: 'never',
+       jsx: 'never',
+       ts: 'never',
+       tsx: 'never',
+     }
+   ]
  },
```
```diff_javascript:eslintrc.js
+ settings: {
+   'import/resolver': {
+     node: {
+       paths: ['src'],
+       extensions: ['.js', '.jsx', '.ts', '.tsx']
+     }
+   }
+ }
```

#### react/jsx-filename-extension [:link: ](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-filename-extension.md)

JSXを含むファイルの拡張子を制限するルール。 `.tsx` なんぞ知らんぞと言われてるのでルール追記。

```diff_javascript:eslintrc.js
+   'react/jsx-filename-extension': [
+     'error',
+     {
+       extensions: ['.jsx', '.tsx'],
+     }
+   ],
```

#### react/react-in-jsx-scope [:link:](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/react-in-jsx-scope.md)
JSXを使用する場合に `React` のインポートを強制するルール
ver17.0から JSXの変換方式変わったのでいらない。のでoffる

```diff_javascript:eslintrc.js
+   'react/react-in-jsx-scope': 'off',
```

#### no-void [:link:](https://eslint.org/docs/rules/no-void)
`reportWebVitals.ts` で `no-floating-promises` 回避のために void 演算子 使います
Effect Hook でも void 使う箇所ある

変数の代入や式の戻り値 以外のでの使用 （すなわち文としての使用）ではおｋとする

```diff_javascript:eslintrc.js
+   'no-void': [
+     'error',
+     {
+       allowAsStatement: true,
+     }
+   ]
```

### ソース修正

#### 全体
import React 消す

```diff_tsx
- import React from 'react';
```

#### App.tsx
アロー関数にして VFCつけて上げる

```diff_tsx:App.tsx
-function App() {
-  return (
+const App: VFC = () => (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.tsx</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
-}
```

#### reportWebVitals.ts
void つけてあげる

```diff_typescript:reportWebVitals.ts
+const reportWebVitals = (onPerfEntry?: ReportHandler): void => {
  if (onPerfEntry && onPerfEntry instanceof Function) {
+    void import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }): void => {
      getCLS(onPerfEntry);
      getFID(onPerfEntry);
      getFCP(onPerfEntry);
      getLCP(onPerfEntry);
      getTTFB(onPerfEntry);
    });
  }
};
```

```bash
$yarn lint    
yarn run v1.22.10
$ eslint --ext .ts,.tsx ./src
✨  Done in 6.77s.
```
`prettier --write` も忘れずに


## VSCode :vs: 
ファイル保存時に自動整形したい。
とりあえず、拡張機能はインストールしておきましょう。
[ESLint - Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)
[Prettier - Code formatter - Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)

### eslint

```json:settings.json
{
    "eslint.packageManager": "yarn",
    "editor.codeActionsOnSave": {
      "source.fixAll.eslint": true
    },
}
```

### prettier
ts, tsxのみな設定

```diff_json:settings.json
{
    ...
+   "[typescript]": {
+       "editor.defaultFormatter": "esbenp.prettier-vscode",
+       "editor.formatOnSave": true
+   },
+   "[typescriptreact]": {
+       "editor.defaultFormatter": "esbenp.prettier-vscode",
+       "editor.formatOnSave": true
+   }
}
```

## おわりに :tea:
とりあえず「Linｔエラー解消目的な構成」なので rules とか tsconfig.json とか適宜調整が必要です。

とはいっても、ただルールをoffるのではなく、 「〇〇の場合のみ許可する」みたいな設定にしたほうがよいと思うので経験に基づくセンスが問われますね。 
それこそ、現場によって様々だと思います。 
あなたの秘伝のタレ（`.eslintrc`）、教えていただけるとうれしいです:smile:



## 参考 :love_letter:

- [くるみ割り書房 ft. React - BOOTH](https://oukayuka.booth.pm/)
- [React + TypeScriptのESLintルールをカスタマイズしたり、Airbnbのやつを導入するぞ。 | Ginpen.com](https://ginpen.com/2019/08/06/eslint-for-react-in-typescript/)
- [【React】create-react-app後、eslint系の設定をeslintrcに移した際のメモ - zackey推し](https://kic-yuuki.hatenablog.com/entry/2019/09/08/111817)
- [React × TypeScript × ESLint × Prettier 環境構築 | Qrunch（クランチ）](https://qrunch.net/@jintz/entries/ys2QoqQ06qEoDHA7)
- [React + Jest + VS Code 環境でESLint の簡易設定をする - Qiita](https://qiita.com/mozukuzuku/items/220db585e1da0392a9e5#jest%E7%92%B0%E5%A2%83%E3%81%AB%E3%81%8A%E3%81%84%E3%81%A6eslint%E3%82%92%E8%A8%AD%E5%AE%9A%E3%81%99%E3%82%8B)