既存ReactプロジェクトにTypeScriptを導入した話

アソビュー Advent Calendar 2019の2日目。

フロントエンドエンジニアの指田です。
今回はアソビューの既存ReactプロジェクトにTypeScriptを導入した内容についてお話します。

はじめに

Babelでコンパイルしている既存ReactプロジェクトにTypeScriptを導入した話です。
本記事ではTypeScript等の説明は致しません。

導入

  • 既存のビルド構成は変更しない。(@babel/preset-typescriptを利用。ts-loaderは使用しない)
  • 導入直後はコード変更もしない。
  • 完全にTypeScriptにするまでJavaScriptとTypeScriptは混在する。

インストール

JavaScript Standard Styleを利用しているため、Typescriptのconfigも追加してます。

# TypeScript
yarn add -D typescript 

# Babel
yarn add -D @babel/preset-typescript

# ESLint
yarn add -D @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint-config-standard-with-typescript

設定

最初の設定は悩みました。
設定を厳しくしすぎると、開発が進まなくなるので必要そうな設定のみで導入しました。

TypeScript

初期はallowJs(JavaScriptもトランスパイルに含めるかどうか)は外して導入してます。

{
  "compilerOptions": {
    "skipLibCheck": true,
    "isolatedModules": true,
    "esModuleInterop": true,
    "strict": true,
    "noImplicitAny": false,
    "noImplicitThis": true,
    "strictNullChecks": true,
    "noImplicitReturns": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
  },
}

ESLint

overridesを使用してJavaScriptもTypeScriptも同じ様にlintが効くようにしてます。

{
  "env": {
    "browser": true,
    "jest": true
  },
  "extends": [
    "standard",
    "standard-with-typescript",
    "standard-jsx",
    "plugin:react/recommended",
    "plugin:css-modules/recommended"
  ],
  "plugins": [
    "react-hooks",
    "css-modules"
  ],
  "rules": {
    "react/prop-types": 0,
    "standard/computed-property-even-spacing": "off",
    "react-hooks/rules-of-hooks": "error"
  },
  "parser": "babel-eslint",
  "settings": {
    "react": {
      "version": "detect"
    },
    "node": {
      "tryExtensions": [".ts", ".tsx", ".js"]
    },
  },
  "overrides": [
    {
      "files": ["*.ts", "*.tsx"],
      "plugins": [
        "@typescript-eslint",
        "react-hooks",
        "css-modules"
      ],
      "parser": "@typescript-eslint/parser",
      "parserOptions": {
        "sourceType": "module",
        "project": "./tsconfig.json"
      },
      "rules": {
        "@typescript-eslint/explicit-member-accessibility": "off",
        "@typescript-eslint/explicit-function-return-type": "off",
        "@typescript-eslint/promise-function-async": [
          "error",
          {
            "allowedPromiseNames": [],
            "checkArrowFunctions": false,
            "checkFunctionDeclarations": true,
            "checkFunctionExpressions": false,
            "checkMethodDeclarations": false
          }
        ]
      }
    }
  ]
}

prettier

{
  "overrides": [
    {
      "files": ["*.ts", "*.tsx"],
      "options": {
        "parser": "typescript"
      }
    }
  ]
}

npm script

JavaScriptも含めたチェックできるtscコマンドも用意しました。

{
  "scripts": {
    "ts-compile-check": "tsc -p tsconfig.json --noEmit",
    "ts-compile-check-in-js": "tsc -p tsconfig.json --noEmit --allowJs",
  },
}

型について

導入直後はanyまたは未指定で進めました。
型を定義していない状態は意味があるのか?とは思いますが、
引数の誤り、不要なPropsがなくなるのでコードがきれいになるメリットがあります。

まとめ

  • 既存コードがある場合、初期の導入範囲はなるべく小さくすることをおすすめします。
  • Babelを使用しているのであれば、@babel/preset-typescriptを利用する方が早めに導入できると思います。
  • ESLint等の設定はoverridesを利用し、混在しても問題ないようにするのがいいと思います。

最後に

アソビューではTypeScriptを導入したプロジェクトはまだ少数なので全てのReactプロジェクトへ導入を検討して行きたいです。