create-react-appを卒業して自分でReact + TypeScriptの開発環境を作れるようになるということ

これまでReactの環境構築をする時は[create-react-app](https://github.com/facebook/create-react-app)に頼りっきりでしたが、いい加減自分で作れないとまずいなと思い忘備録も兼ねて残しておきます。
また、せっかくTypeScriptも使うので `webpack.config.js` もTypeScriptで書けるようにしたいと思います。

***

最終的なディレクトリ構成は次のようになります。

```
.
┃━━ public
┃  ┗━ index.html
┃━━ src
┃  ┃━ index.tsx
┃  ┗━ App.tsx
┃━━ package.json
┃━━ package-lock.json
┃━━ tsconfig.json
┗━━ webpack.config.ts
```

それではやっていきましょう。

## 1. ディレクトリの初期化

適当なディレクトリを作って `npm init` するだけです。

```
mkdir hogehoge
cd hogehoge
npm init -y
```

`-y` オプションはお好みで（つけると全部初期値で自動的に初期化されます）。
初期化が終了すると `package.json` ができているはずです。

## 2. モジュールのインストール

React + TypeScriptの環境構築なので、とりあえず必要そうなモジュールをインストールしていきます。
バンドラにはWebpackを利用します。

```
npm i -d react react-dom
npm i -D typescript ts-loader webpack webpack-cli webpack-dev-server @types/node @types/react @types/react-dom
```

`@types/node` の中に `webpack.Configuration` が定義されており、これがWebpackの設定情報の型です。
これを使って `webpack.config.ts` を書いていきます。

```webpack.config.ts
import { Configuration } from 'webpack'
import path from 'path'

const config: Configuration = {
  mode: 'development',
  entry: './src/index.tsx',
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: 'ts-loader',
        exclude: /node_modules/,
      },
    ],
  },
  resolve: {
    extensions: [
      '.tsx',
      '.ts',
      '.js',
    ],
  },
  devtool: 'inline-source-map',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
  },
}

export default config
```

感のいい人はお気づきかもしれませんが、現段階だと開発用サーバーの設定である `devServer` プロパティがありません（書いてみるとエラーになるはずです）。
そこで `@types/webpack-dev-server` をインストールします。

```
npm i -D @types/webpack-dev-server
```

これで `webpack-dev-server` の設定ができるようになりました。 `webpack.config.ts` に設定を追記します。

```webpack.config.ts
const config: Configuration = {
  // ...
  devServer: {
    contentBase: path.resolve(__dirname, 'dist'),
  },
  // ...
}
```

これでエラーが消えたはずです。

### `HtmlWebpackPlugin` の設定

詳しくは割愛しますが、このプラグインを使うとテンプレートのHTMLのbodyタグの末尾にscriptタグを勝手に挿入してくれるので便利です。
というわけでインストールします。

```
npm i -D html-webpack-plugin @types/html-webpack-plugin
```

```webpack.config.ts
import HtmlWebpackPlugin from 'html-webpack-plugin'

const config: Configuration = {
  // ...
  plugins: [
    new HtmlWebpackPlugin({
      template: './public/index.html',
    }),
  ],
  // ...
}
```

テンプレートとなるHTMLファイルを `public/index.html` に作成します。

``` public/index.html
<html lang="ja">
  
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>your app title</title>
</head>

<body>
  <div id="root"></div>
</body>

</html>
```

ここではテンプレートHTMLのパスしか設定しませんが、 `title` など他にもいくつかオプションがあるので気になる方は調べてみてください。

## 3. TypeScriptの設定

`tsconfig.json` を作成・編集します。

```tsconfig.json
{
  "compilerOptions": {
    "outDir": "./dist/",
    "noImplicitAny": true,
    "module": "es6",
    "target": "es5",
    "jsx": "react",
    "allowJs": true,
    "allowSyntheticDefaultImports": true
  }
}
```

`src` ディレクトリ内に、エントリポイントとなる `index.tsx` を作っていきます。とりあえず簡単なものだけ。

```src/App.tsx
import React, { FC } from 'react'

export const App: FC = () => <div>Hello World!</div>
```

```src/index.tsx
import React from 'react'
import ReactDOM from 'react-dom'
import { App } from './App'

ReactDOM.render(<App />, document.getElementById("root"))
```

## 4. npm scriptsを設定する

`package.json` を編集します。
開発用サーバーを立ち上げるコマンドと、ビルド用のコマンドを用意します。

```package.json
{
  // ...
  "scripts": {
    "dev": "webpack-dev-server --open",
    "build": "webpack"
  },
  // ...
  }
}
```

ここまででほぼ完成みたいなものですが、このままだとコンパイルできません。

## 5. 設定ファイルを読み込めるようにする

今のままだと設定ファイルが `.ts` ファイルなので読み込めません。そこで `ts-node` というモジュールを使用します。
モジュール名の通りですが、Node.jsがTypeScriptを直接読めるようにするものです。

```
npm i -D ts-node
```

これでコンパイルが通る・・・ようにはなりません。もう1ステップ必要です。
作成した `webpack.config.ts` ですが、import/export文を使用しているためこのままだと使えません。
これが使えるように `tsconfig.json` を編集します。

```tsconfig.json
{
  "compilerOptions": {
    // ...
    "esModuleInterop": true,
    "module": "commonjs",
    // ...
  }
}
```

詳細な説明は省きますが、「CommonJSモジュールだよ～」と教えてあげることで使えるようになります。

とりあえずはこれにて終了です。`npm run dev` なり `npm run build` して開発に励みましょう。

## 6. おわりに

ところどころ端折りましたが、これでReact + TypeScriptの開発環境は作れたはずです。
似たような記事は結構あるのですが、同じReact + TypeScriptでもWebpackの設定までTypeScriptで行っているものは少なかったので記事にしました。型定義があるぶん補完も効くので書きやすいです。

こんな記事でも誰かのお役に立てば幸いです。ご覧頂きありがとうございました。