create-react-appで React + Typescript な環境を構築する

# はじめに

facebook社公式のReactプロジェクト生成ツールである [create-react-app](https://create-react-app.dev/) が
2018/10/29リリースの [v2.1.0](https://github.com/facebook/create-react-app/releases/tag/v2.1.0) でTypeScriptをサポートするようになりました。

とても簡単に React + Typescript の開発環境を構築できるようになっているので、その方法をまとめています。
よりよい方法やバグ等ございましたら、アドバイスいただけると光栄です。

尚、執筆時のバージョンは以下のようになっております。
最新の方法について知りたい方は [公式ドキュメント](https://create-react-app.dev/docs/adding-typescript/) をご覧ください。

```bash
$ node -v
v13.9.0

$ yarn -v
1.22.0

$ npx create-react-app --version
3.3.0
```

# 環境構築

## 0. 事前準備

各種インストールの確認をします。

### 0-1. Node.js

下記コマンドで、Node.js がインストールされているか確認します。

```bash
$ node -v
v13.9.0
```

まだインストールされていない場合は、[公式ドキュメント](https://nodejs.org/ja/download/) を参照の上、インストールしてください。

※ MacOSの方は以下の方法もおすすめです。
[MacにNode.jsをインストール（anyenv + nodenv編） - Qiita](https://qiita.com/kyosuke5_20/items/eece817eb283fc9d214f)

### 0-2. yarn

パッケージマネジャーは yarn を使用するため、
下記コマンドで、yarn がインストールされているか確認します。

```bash
$ yarn -v
1.22.0
```

まだインストールされていない場合は、[公式ドキュメント](https://classic.yarnpkg.com/ja/docs/install/) を参照の上、インストールしてください。

## 1. 新規プロジェクトを作成

実際に create-react-app でプロジェクトを作成します。

### 1-1. create-react-app

以下のコマンドを叩くだけでプロジェクトを作成できます。

```bash
$ npx create-react-app {プロジェクト名} --template typescript
```

`{プロジェクト名}`には、自分のつけたいプロジェクト名を設定してください。
例えば、`react-ts-app` というプロジェクトを作成したい場合は、以下のコマンドを入力します。

```bash:例）
$ npx create-react-app react-ts-app --template typescript
```

`Happy hacking!` という文字が表示されれば完了です。

### 1-2. プロジェクトを起動

作成したプロジェクトのディレクトリに移動し、起動します。

```bash
$ cd {プロジェクト名}
$ yarn start
```

先ほどの例に挙げた `react-ts-app` というプロジェクトを作成した場合は、以下のコマンドを入力します。

```bash:例）
$ cd react-ts-app
$ yarn start
```

http://localhost:3000 がブラウザで開き、以下のような画面が表示されれば完了です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198450/54690077-31b4-d2eb-9a46-f63450c7cfb2.png)

一度、`Ctrl + C` で終了します。

## 2. 開発環境設定

開発するに当たり便利な各種設定をします。

### 2-1. コミット時にコードを自動整形する

コミット時にコードを自動整形したいので、下記３つをインストールします。

* husky
* lint-staged
* prettier

以下のコマンドを入力すれば、インストールができます。
開発時のみに必要なライブラリなので、`-D` を付けてすべて `devDependencies` としてインストールします。

```bash
$ yarn add -D husky lint-staged prettier
```

※ dependencies と devDependencies の違いについては、下記を参照ください。
[package.jsonにおけるdependenciesとdevDependenciesの違い(超シンプルに) - Qiita](https://qiita.com/yfujii1127/items/0b7fc2bc826df23935db)

インストールが完了したら、`package.json` を開いて必要なものを足していきます。

#### 2-1-1. lint-staged

拡張子が `ts` または `tsx` の場合に、`prettier` を走らせてから `git add` するように、`package.json` の最後あたりに下記を追記します。

```json:pakege.json
{
  ...,
  "lint-staged": {
    "*.{ts,tsx}": [
      "prettier --write"
    ]
  }
}
```

##### ※2020/11/20追記

更新しようとしたら、下記のようなメッセージが出ました。

```bash
husky > pre-commit (node v12.18.3)
⚠ Some of your tasks use `git add` command. Please remove it from the config since all modifications made by tasks will be automatically added to the git commit index.

No staged files match any of provided globs.
[master 1234***] 修正前
 1 file changed, 8 insertions(+), 5 deletions(-)
```

「git add commandは不要だから取り除いてね」 と警告が出ています。
とりあえず削除してみました。


```bash
husky > pre-commit (node v12.18.3)
No staged files match any of provided globs.
[master 4567***] git add を削除
 1 file changed, 1 insertion(+), 2 deletions(-)
```

いい感じみたいですね。
サンプルも変更しました。

#### 2-1-2. husky

`precommit` 時に上記の `lint-staged` が実行されるように設定します。

##### ※2021/04/01追記

huskyのv5から設定方法がガラッと変わったようで、バージョンによって別の設定が必要になりました。

###### ----- v5以降 ------

下記コマンドを実行します。

```bash
$ npx husky install
$ npx husky add .husky/pre-commit "yarn lint-staged"
```

`package.json` の `scripts` に下記を追記します。

```json:pakege.json
{
  ...,
  "scripts": {
    ...,
    "prepare": "husky install",
    "lint-staged": "lint-staged"
  }
}
```


###### ----- v4以前 -----

`package.json` の最後あたりに下記を追記します。

```json:pakege.json
{
  ...,
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  }
}
```



#### 2-1-3. prettier

コードフォーマット関連の設定を追加します。
こちらも、`package.json` の最後あたりに下記を追記します。

```json:pakege.json
{
  ...,
  "prettier": {
    "semi": false,
    "singleQuote": true,
    "tabWidth": 2,
    "trailingComma": "all"
  }
}
```

ここでは以下のような設定をしています。

* `semi` : 末尾にセミコロンをつけるかどうか
* `singleQuote` : シングルクォートに変換するかどうか
* `tabWidth` : インデントのスペースの数
* `trailingComma` : 複数行の場合に末尾にカンマをつけるか

※ その他のオプションについては、prettierの [公式ドキュメント](https://prettier.io/docs/en/options.html) で確認できます。

### 2-2. 型チェックによる安全性を最大化する

せっかく TypeScript を使用しているので、

* 型チェックによる安全性を最大化したい
* あわよくば、不要なコードも怒ってくれるとうれしいなー

ということで、`tsconfig.json` の `compilerOptions` に下記を追加します。


```json:tsconfig.json
{
  "compilerOptions": {
    ...,
    strict: true,
    ...,
    "noImplicitReturns": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
  },
  ...
}
```

ここでは、下記のオプションを追加しました。

* ~~`strictNullChecks` : nullやundefinedが「一人前の型」になる。~~
* ~~`noImplicitAny` : 暗黙のany型を禁止する。~~
* ~~`noImplicitThis` : thisに型を指定していない場合にエラーを発生させる。~~
* `noImplicitReturns` : 関数内のすべての経路で、返り値の型があっているかをチェックする。
* `noUnusedLocals` : 宣言して、一度も使われないローカル変数があると、コンパイルエラーになる。
* `noUnusedParameters` : 一度も使われないパラメータがあると、コンパイルエラーになる。

※ その他のオプションについては、TypeScriptの [公式ドキュメント](https://www.typescriptlang.org/docs/handbook/compiler-options.html) で確認できます。

##### ※2020/11/20追記

確認したところ、すでに `strict` オプションが `true` になっていました。
このオプションは [公式ドキュメント](https://www.typescriptlang.org/docs/handbook/compiler-options.html) で以下のような説明があります。

> Enable all strict type checking options.
Enabling --strict enables --noImplicitAny, --noImplicitThis, --alwaysStrict, --strictBindCallApply, --strictNullChecks, --strictFunctionTypes and --strictPropertyInitialization.

どうやらこのオプションには、 下記オプションが含まれているとのことです。

- `noImplicitAny`
- `noImplicitThis` 
- `alwaysStrict`
- `strictBindCallApply`
- `strictNullChecks`
- `strictFunctionTypes`
- `strictPropertyInitialization`

下記が被っているので設定から外しておきます...
`noImplicitAny` と `noImplicitThis` と `strictNullChecks`

### 2-3. import を絶対パスにする

相対パスで import するのは色々と面倒なので、絶対パスで指定できるようにします。

以下のように `tsconfig.json` ファイルの `compilerOptions` に `baseUrl` を追加します。

```json:tsconfig.json
{
  "compilerOptions": {
    ...,
    "baseUrl": "src"
  },
  ...
}
```

これで、絶対パス使えるようになりました。
例えば、`src/components/Button.tsx` にあるモジュールをインポートする場合、以下のようにインポートできます。

```js:例）
import Button from 'components/Button'
```

※ 詳しい方法は [公式ドキュメント](https://create-react-app.dev/docs/importing-a-component/#absolute-imports) で確認できます。

# 終わりに

いかがでしょうか？
これを基に快適な React + TypeScript ライフを楽しんでください！

また、こちらの方法で作成したプロジェクトのサンプルをGitHubに公開していますので、よろしければ参照ください。

https://github.com/genki-sano/react-ts-app

最後まで読んでいただいてありがとうございます。
添削や過不足あれば、お気軽にご指摘ください。

##### ※2021/11/20追記
huskyのv4以前の場合のブランチを作成しました。
古いバージョンで作成された場合はこちらを参照してください。

https://github.com/genki-sano/react-ts-app/tree/feature/before-haskey-v4

# 参考

* [Create React App · Set up a modern web app by running one command.](https://create-react-app.dev/)
* [Release v2.1.0 · facebook/create-react-app](https://github.com/facebook/create-react-app/releases/tag/v2.1.0)
* [Options · Prettier](https://prettier.io/docs/en/options.html)
* [Compiler Options · TypeScript](https://www.typescriptlang.org/docs/handbook/compiler-options.html)
* [ダウンロード | Node.js](https://nodejs.org/ja/download/)
* [インストール | Yarn](https://classic.yarnpkg.com/ja/docs/install/)
* [MacにNode.jsをインストール（anyenv + nodenv編） - Qiita](https://qiita.com/kyosuke5_20/items/eece817eb283fc9d214f)
* [package.jsonにおけるdependenciesとdevDependenciesの違い(超シンプルに) - Qiita](https://qiita.com/yfujii1127/items/0b7fc2bc826df23935db)
* [Husky - Git hooks](https://typicode.github.io/husky/#/?id=install)