Closed8

写経・整理 | りあクト! TypeScriptで始めるつらくないReact開発 第3.1版【Ⅱ. React基礎編】

ryskitryskit

Yarn

npmよりも後にFacebook、Google、Exponent、Tildeによって開発されたJavaScriptのパッケージマネージャー。

インストール

$ npm install yarn
ryskitryskit

ESLint

Find and fix problems in your JavaScript code
https://eslint.org/docs/user-guide/getting-started

$ yarn eslint --init

? How would you like to use ESLint? …
    ✓ To check syntax, find problems, and enforce code style
? What type of modules does your project use? …
    ✓  JavaScript modules (import/export)
? Which framework does your project use? …
    ✓ React
? Does your project use TypeScript?
    ✓ Yes
? Where does your code run?
    ✓ Browser
? How would you like to define a style for your project?
    ✓ Use a popular style guilde
? Which style guide do you want to follow?
    ✓ Airbnb: https://github.com/airbnb/javascript
? What format do you want your config file to be in?
    ✓ JavaScript
? Would you like to install them now with npm?
    ✓ No

出力された .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": {
    }
};
ryskitryskit
$ yarn add -D eslint-plugin-react @typescript-eslint/eslint-plugin \
eslint-config-airbnb eslint-plugin-import eslint-plugin-jsx-a11y \
eslint-plugin-react-hooks @typescript-eslint/parser

yarn add <package...> -D
-D|--dev オプションを指定することで、devDependenciesにパッケージ追加する

$ typesync

https://github.com/jeffijoe/typesync

Install missing TypeScript typings for dependencies in your package.json.
package.jsonに含まれる依存関係で欠けているTypeScrptの型定義ファイルをインストールする

$ yarn typesync
ryskitryskit
module.exports = {
    env: {
      browser: true,
      es2021: true,
    },
    extends: [
      'plugin:react/recommended',
      'airbnb',
      'airbnb/hooks',
      'plugin:import/errors',
      'plugin:import/warnings',
      'plugin:import/typescript',
      'plugin:@typescript-eslint/recommended',
      'plugin:@typescript-eslint/recommended-requiring-type-checking',
    ],
    parser: '@typescript-eslint/parser',
    parserOptions: {
      ecmaFeatures: {
        jsx: true,
      },
      ecmaVersion: 12,
      project: './tsconfig.eslint.json',
      sourceType: 'module',
      tsconfigRootDir: __dirname,
    },
    plugins: [
      '@typescript-eslint',
      'import',
      'jsx-a11y',
      'react',
      'react-hooks',
    ],
    root: true,
    rules: {
      // occur error in `import React from 'react'` with react-scripts 4.0.1
      'no-use-before-define': 'off',
      '@typescript-eslint/no-use-before-define': [
        'error',
      ],
      'lines-between-class-members': [
        'error',
        'always',
        {
          exceptAfterSingleLine: true,
        },
      ],
      'no-void': [
        'error',
        {
          allowAsStatement: true,
        },
      ],
      'padding-line-between-statements': [
        'error',
        {
          blankLine: 'always',
          prev: '*',
          next: 'return',
        },
      ],
      '@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'],
        },
      ],
      'react/jsx-props-no-spreading': [
        'error',
        {
          html: 'enforce',
          custom: 'enforce',
          explicitSpread: 'ignore',
        },
      ],
      'react/react-in-jsx-scope': 'off',
    },
    overrides: [
      {
        'files': ['*.tsx'],
        'rules': {
          'react/prop-types': 'off',
        },
      },
    ],
    settings: {
      'import/resolver': {
        node: {
          paths: ['src'],
        },
      },
    },
  };

extends に記述するのは各プラグインの推奨の共有設定で、記述される順番には意味があるため注意。
共有設定間で設定ルールの値が衝突したら、後に記述したものが先に記述されたものを上書きする(後勝ち)

plugins には読み込ませる追加のプラグインを記述する。
yarn add して依存関係に追加するだけでは有効にならず、設定ファイルにプラグイン名を記述する必要がある。

rules には各ルールの適用の可否やエラーレベルを設定する。
https://eslint.org/docs/rules/

ryskitryskit

.eslintignore ファイルはESLintのチェックの対象外となるファイルを定義する。

[.eslintignore]

build/
public/
**/coverage/
**/node_modules/
**/*.min.js
*.config.js
.*lintrc.js
ryskitryskit

Prettier

https://prettier.io/

ESLint の環境に Prettier を加えるのに必要なパッケージは2つ

  • prettier
    • Prettier 本体
  • eslint-config-prettier
    • Prettier と強豪する可能性のある ESLint の各種ルールを無効にする共有設定
$ yarn add -D prettier eslint-config-prettier
$ yarn typesync
$ yarn

.eslintrc.js の設定項目である extendsprettier を追加する。

.prettierrc という名前でプロジェクトルートにファイルを作成する。

[.prettierrc]

singleQuote: true
trailingComma: "all"
ryskitryskit

create-react-appで生成したデフォルトのコードに対してESLintを実行すると src/reportWebVitals.ts でエラーが検知された。

エラー内容は以下の通り。

  3:25  warning  Missing return type on function                                                                  @typescript-eslint/explicit-module-boundary-types
  5:5   error    Promises must be handled appropriately or explicitly marked as ignored with the `void` operator  @typescript-eslint/no-floating-promises

warningはひとまず置いておいて、errorである @typescript-eslint/no-floating-promises の方を解消する。

[before]

import { ReportHandler } from 'web-vitals';

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

export default reportWebVitals;

import('web-vitals').then(...) の返り値の型がないから何か適切な処理をするか、 void を指定しなさい」と怒られているので、とりあえず void を指定するとエラーを解消することができた。

あと、warningの方は3行目の関数の返り値の型がないから指定しておいてねと言われているので指定すると解消できた。

修正後は以下の通り。

[after]

import { ReportHandler } from 'web-vitals';

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

export default reportWebVitals;

フロントエンジニアをされている方々は、ESLintとかサクッと設定しちゃうんだろうか?
慣れてないのもあるけど、他の言語のLint設定より難しく感じる。。。

ryskitryskit

2019年2月リリースのバージョン 16.8.0 からは、関数コンポーネント(Function Component)で記述することが推奨されるようになった。

Reactの関数コンポーネントの型インターフェースには FunctionComponent が用意されている。
VFC とエイリアスされている VoidFunctionComponent インターフェースもある。

従来の FC で定義された関数コンポーネントでは、子要素の操作が必要ない場合でも暗黙のうちに propsの中に子要素のオブジェクトが渡されていた。

この VoidFunctionComponent は子要素をいじらない関数コンポーネントの型定義に使うことが推奨されている。

https://react-typescript-cheatsheet.netlify.app/

このスクラップは2023/01/12にクローズされました