TypeScriptでReact & Webpackを使う

TypeSciptのドキュメントに[Reactを使用するドキュメント](https://www.typescriptlang.org/docs/handbook/react-&-webpack.html)があったので、試してみました。

ドキュメントの通りに行ってもエラーが表示されるので、試行錯誤の末に動作するようになったものを記載します。

# 動作確認済み環境
- node.js 7.9.0
- npm 4.2.0
- macOS High Sierra

# 作成手順
## プロジェクトディレクトリの作成
プロジェクトディレクトリを新規で作成します。

```
mkdir proj
cd proj
mkdir src
cd src
mkdir components
cd ..
```

最終的に以下のような形になるようにディレクトリを作成します。

```
proj/
└─ src/
   └─ components/
```

## プロジェクトの初期化

```
npm init
```

## 依存関係のライブラリのインストール

まず、TypeScriptとWebpackをインストールします。

```
npm install -g typescript webpack webpack-cli
```

ドキュメントでは以下のようになっていますが、WebpackのバージョンアップによりWebpack本体とコマンドラインツールが分割されたのでこのままでは動作しません。

```
npm install -g webpack
```

次にReactと型定義ファイルをインストールします。
これらは実行時にも使用します。

```
npm install --save react react-dom @types/react @types/react-dom
```

最後に開発時のみ使用するソースマップなどをインストールします。

```
npm install --save-dev typescript awesome-typescript-loader source-map-loader
```

## tsconfigの追加
TypeScriptのビルド設定を行うtsconfig.jsonをプロジェクトフォルダの直下に作成します。
tsconfig.jsonに以下を記載します。

```json:tsconfig.json
{
  "compilerOptions": {
    "sourceMap": true,
    // TSはECMAScript 5に変換
    "target": "es5",
    // TSのモジュールはES Modulesとして出力
    "module": "es2015",
    // JSXの書式を有効に設定
    "jsx": "react",
    "moduleResolution": "node",
    "lib": [
      "es2017",
      "dom"
    ]
  }
}
```

## コードの追加
src/componentsにHello.tsxを追加します。

``` TypeScript:Hello.tsx
import * as React from "react";

export interface HelloProps { compiler: string; framework: string; }

export const Hello = (props: HelloProps) => <h1>Hello from {props.compiler} and {props.framework}!</h1>;
```
次にsrc以下にindex.tsxを追加します。

``` typescript:index.tsx
import * as React from "react";
import * as ReactDOM from "react-dom";

import { Hello } from "./components/Hello";

ReactDOM.render(
    <Hello compiler="TypeScript" framework="React" />,
    document.getElementById("example")
);
```

最後にプロジェクトフォルダ直下にindex.htmlを追加します。

```html:index.html
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title>Hello React!</title>
    </head>
    <body>
        <div id="example"></div>

        <!-- Dependencies -->
        <script src="./node_modules/react/umd/react.development.js"></script>
        <script src="./node_modules/react-dom/umd/react-dom.development.js"></script>

        <!-- Main -->
        <script src="./dist/bundle.js"></script>
    </body>
</html>
```

## Webpackの設定
Webpackの設定ファイルwebpack.config.jsをプロジェクトフォルダ直下に作成します。

```javascript:webpack.config.js
module.exports = {
    entry: "./src/index.tsx",
    output: {
        filename: "bundle.js",
        path: __dirname + "/dist"
    },

    // Enable sourcemaps for debugging webpack's output.
    devtool: "source-map",

    resolve: {
        // Add '.ts' and '.tsx' as resolvable extensions.
        extensions: [".ts", ".tsx", ".js", ".json"]
    },

    module: {
        rules: [
            // All files with a '.ts' or '.tsx' extension will be handled by 'awesome-typescript-loader'.
            { test: /\.tsx?$/, loader: "awesome-typescript-loader" },

            // All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'.
            { enforce: "pre", test: /\.js$/, loader: "source-map-loader" }
        ]
    },

    // When importing a module whose path matches one of the following, just
    // assume a corresponding global variable exists and use that instead.
    // This is important because it allows us to avoid bundling all of our
    // dependencies, which allows browsers to cache those libraries between builds.
    externals: {
        "react": "React",
        "react-dom": "ReactDOM"
    },
};
```

最近の流れとして、npmでビルドなどを行わせる方がよいのでpackage.jsonに以下を追加します。

```json:package.json
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    //ここから
    "build": "webpack",
    "watch": "webpack -w"
　　　　　　　　//ここまでを追加する
  },
```

## 実行

` npm run build`を実行するとTypeScriptのコンパイルから最小化まで実行してくれます。