Closed21

React × Typescript × ESLint × Prettier × VSCodeなSetup 2021

sh090sh090

とりあえずプロジェクト作成

yarn create react-app react-app --template typescript

package.jsonの中身

package.json
{
  "name": "react-app",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^5.11.4",
    "@testing-library/react": "^11.1.0",
    "@testing-library/user-event": "^12.1.10",
    "@types/jest": "^26.0.15",
    "@types/node": "^12.0.0",
    "@types/react": "^17.0.0",
    "@types/react-dom": "^17.0.0",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "react-scripts": "4.0.3",
    "typescript": "^4.1.2",
    "web-vitals": "^1.0.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

sh090sh090

前記事との各種バージョンの差分

before after
react 16.13.0 17.0.2
react-scripts 3.4.0 4.0.3
eslint 6.8.0 7.20.0
typescript 3.7.2 4.1.2
sh090sh090

React v17.0 での変更点

v16

変換前
import React from 'react';

function App() {
  return <h1>Hello World</h1>;
}
変換後
import React from 'react';

function App() {
  return React.createElement('h1', null, 'Hello world');
}

v17

変換前
function App() {
  return <h1>Hello World</h1>;
}
変換後
import {jsx as _jsx} from 'react/jsx-runtime';

function App() {
  return _jsx('h1', { children: 'Hello world' });
}

Reactのimportが必要なくなった

TypeScriptを使用する場合は v4.1から使える
Announcing TypeScript 4.1 | TypeScript

sh090sh090

eslint --init

前記事との変更点

  • eslintrc のファイル形式を json から javascriptに変更
yarn run eslint --init
✔ 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

生成される .eslintrc.js

.eslintrc.js
module.exports = {
    "env": {
        "browser": true,
        "es2021": true
    },
    "extends": [
        "plugin:react/recommended",
        "airbnb"
    ],
    "parser": "@typescript-eslint/parser",
    "parserOptions": {
        "ecmaFeatures": {
            "jsx": true
        },
        "ecmaVersion": 12,
        "sourceType": "module"
    },
    "plugins": [
        "react",
        "@typescript-eslint"
    ],
    "rules": {
    }
};
sh090sh090

eslint 関連のpackage入れていく

typescript-eslint

これは変わんない

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

airbnb

前記事では airbnbのパッケージのみインストールしてたけど
依存パッケージもインストールしてみることにする(公式に習って)

before

yarn add -D eslint-config-airbnb

aflter

eslint-config-airbnb ← 参照
以下のコマンドで依存関係のパッケージをいい感じにインストールしてくれる。
※ npm の ver 5< が必要

npx install-peerdeps --dev eslint-config-airbnb

yarn を使ってる場合は 、質問されるので y を選択。

It seems as if you are using Yarn. Would you like to use Yarn for the installation?
package.json
  "devDependencies": {
    "@typescript-eslint/eslint-plugin": "^4.22.0",
    "@typescript-eslint/parser": "^4.22.0",
    "eslint": "^7.2.0",
    "eslint-config-airbnb": "18.2.1",
    "eslint-plugin-import": "^2.22.1",
    "eslint-plugin-jsx-a11y": "^6.4.1",
    "eslint-plugin-react": "^7.21.5",
    "eslint-plugin-react-hooks": "^1.7.0"
  }
sh090sh090

eslintrc の設定

すこしめんどいけど yarn eslint --print-config しつつ 見ていく

初期状態

eslintrc.js
"extends": [
    'plugin:react/recommended',
    'airbnb',
],

カンケーないけど plugin:react/recommended の記述消しても rule自体は変わらない
airbnb が 踏襲してるからだと思う。 まぁ残すけど

eslintrc.js
"extends": [
-   'plugin:react/recommended',
    'airbnb',
],

eslint:recommendedairbnb のruleに網羅されてるので 追加しても変わんない
よって↓の記述いらない

eslintrc.js
"extends": [
    'plugin:react/recommended',
+   'eslint:recommended',
    'airbnb',
],

eslint-config-airbnb/hooks を設定してみる

公式のこれ
React Hooksのルールを追加してくれる

eslintrc.js
  extends: [
    'plugin:react/recommended',
    'airbnb',
+   'airbnb/hooks',
  ],
出力されるrule
  "plugins": [
    "import",
    "jsx-a11y",
+   "react-hooks",
    "@typescript-eslint",
    "react"
  ],
  "rules": {
+   "react-hooks/rules-of-hooks": [
+     "error"
+   ],
+   "react-hooks/exhaustive-deps": [
+     "error"
+   ],
...
}
sh090sh090

eslintrc の設定②

@typescript-eslint/eslint-plugin

公式
前記事との違い

  • plugin:@typescript-eslint/eslint-recommended の記述いらないかも
  • plugin:@typescript-eslint/recommended-requiring-type-checking をextendsに追加
eslintrc.js
  extends: [
    'plugin:react/recommended',
    'airbnb',
    'airbnb/hooks',
+   'plugin:@typescript-eslint/recommended',
+   'plugin:@typescript-eslint/recommended-requiring-type-checking',
  ],

'plugin:@typescript-eslint/recommended' → 型チェックが不要なルール
'plugin:@typescript-eslint/recommended-requiring-type-checking' → 型チェックが必要なルール

parserOptions の設定

extends'plugin:@typescript-eslint/recommended-requiring-type-checking' を指定したので
@typescript-eslint/parser にオプションを渡す必要がある。

typescript-eslint/TYPED_LINTING.md at 26d71b57fbff013b9c9434c96e2ba98c6c541259 · typescript-eslint/typescript-eslint
↑参照

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

さっきのリンクによると、 TypeScript ビルド to eslint 解析となるので でかいプロジェクトだと パフォーマンスに影響有 らしい

sh090sh090

eslintrc の設定③

plugin:import/typescript

eslint-plugin-import - npm
こいつを記述する必要があるのかがわからん

記述時の 出力ルールが謎すぎる。

出力ルール
  "settings": {
    "import/resolver": {
      "node": {
        "extensions": [
          ".js",
          ".jsx",
          ".json",
+         ".js",
+         ".jsx"
        ]
      }
    },
...
    "import/extensions": [
      ".js",
      ".mjs",
      ".jsx",
+     ".js",
+     ".jsx"
    ],
...

rules に 新しくルールが追加されるわけではないみたい

sh090sh090

eslintrc.jsの rules にルール追加

import/resolver , import/extensions

これはかわらない

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

react/jsx-filename-extension

これも変わんない

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

react/react-in-jsx-scope

eslint-plugin-react/react-in-jsx-scope.md
JSXを使用する場合に React のインポートを強制するルール
ver17.0 < の変換方式なのでいらない。のでoffる

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

no-void

no-void - Rules - ESLint - Pluggable JavaScript linter
reportWebVitals.tsno-floating-promises 回避のために void 演算子 使う必要がある
Effect Hook でも void 使う箇所ある

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

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

eslintrcに Prettierの設定を記述

eslint-config-prettier のアップデートに伴い、前記事からちょっと変更がある。

beforeの書き方

eslintrc.js
  extends: [
    'some-other-config-you-use',
+   'prettier/@typescript-eslint',
+   'prettier/react'
  ],

この prettier/@typescript-eslintprettier/react みたいな 環境ごとの記述が
prettier にマージされた。
eslint-config-prettier/CHANGELOG.md at main · prettier/eslint-config-prettier

なので 以下でおk

afterの書き方

eslintrc.js
  extends: [
    'some-other-config-you-use',
+   'prettier'
  ],
sh090sh090

.prettierrc 書く

前記事での書き方

.prettierrc
{
    "trailingComma": "es5",
    "tabWidth": 2,
    "arrowParens": "always"
}

これ全部 v2.0.0 以上だと default 値ですね。。

prettier の設定正直お好みによるところが大きい

craのリポジトリ にならって
"singleQuote": true しとく?

.prettierrc
{
    "singleQuote": true
}

衝突ルール検出

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

問題なし

sh090sh090

npm script

とりあえず lintだけ

package.json
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
+   "lint": "eslint --ext .ts,.tsx ./src",
    "eject": "react-scripts eject"
  },

globで書いてもかっけーすね

"lint": "eslint 'src/**/*.{js,jsx,ts,tsx}'",
sh090sh090

Lintエラー治す

全体

import React 消す

- import React from 'react';

App.tsx

アロー関数にして VFCつけて上げる

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 つけてあげる

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);
    });
  }
};

ほい

$yarn lint    
yarn run v1.22.10
$ eslint --ext .ts,.tsx ./src
✨  Done in 6.77s.
sh090sh090

VSCodeさん なんもわからん

ESLint - Visual Studio Marketplace

eslintとりあえずこんだけ

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

ts, tsxだけ 保存時にprettierでフォーマットかけたい設定はこうですか?

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

.eslintignore 作る

vscode で src配下だけ eslintで見てほしいときの書き方がわからないので作る

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

とりあえず.eslintrc.js でエラーでなくなった

sh090sh090

npm script ②

fix とか formatとか

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"
  },

fix で 「prettier --write してから eslint --fix あてる」

sh090sh090

最小構成なので rules とか tsconfig.json とか適宜いじった方が吉

「React × TypeScriptのおすすめ tsconfig.json の設定方法 」みたいな記事書きたい

sh090sh090

husky は 記事が色々出てるので 今回の記事修正ではやらんです。 余力があったら書きます。

このスクラップは2021/04/22にクローズされました