LoginSignup
39
31

More than 5 years have passed since last update.

create-react-appでTypeScript用のReact開発環境を作る(入門者向け)

Last updated at Posted at 2017-01-07

React Advent Calendar 2016 に空きがあったのでねじ込みました。

仕事ではないけど普段はAngularでフロントエンドを書いています。ちきさんです。

関数型ReactでビジュアルFizzBuzz があまりにも読みやすいソースコードで、読んでるうちに僕もReactが書ける気がしてきたので挑戦してみました。

ところでAngularにはangular-cliという環境構築ツールがあります。
オレオレ環境構築は過去に散々やってきたので、またReactでオレオレは嫌だなと思っていたらツイッターで facebookincubator/create-react-app の存在を教えていただきまして、使ってみたらあっさり環境が出来上がったのでワオと思ったのですがコレ当然なんですけどJavaScript用の環境なんですね。

そのままではTypeScript脳の僕には使えないので仕方なくタイトルのようなことをした次第。そして備忘録も兼ねて記事に残しておこうというわけです。

ちなみに僕が初めて書いたReactアプリは ovrmrw/my-first-react-typescript です。オンラインデモは こちら です。宜しければどうぞ。

ではここから本題です。

create-react-appをインストール

create-react-appという長い名前のCLIをインストールします。もうちょっと名前なんとかならなかったんですかね。
プロジェクト作成時にyarnが使われるので一応yarnも加えておきます。
後々必要になるのでついでにtypescripttslintもインストールしましょう。

$ npm i -g create-react-app yarn typescript tslint

Reactプロジェクトを作る

適当なフォルダに移動して下記のコマンドを実行します。

$ create-react-app my-app

my-appというフォルダが作成されて、そこに必要なものがダウンロードされるでしょう。
結構時間かかります。

npm run ejectで環境を一旦壊す

デフォルトではJavaScript + Babelで開発するようになっていますし、Webpackの設定も含めて諸々の設定ファイルがnode_modulesの中に隠蔽されてしまっています。
TypeScriptで書きたいのでこれでは困ってしまいますね。実はそういう人のためのコマンドが用意されています。

$ npm run eject

これは不可逆な操作なので取扱注意です。ドキュメントにも以下のように書いてあります。

Note: this is a one-way operation. Once you eject, you can’t go back!

しかし我々はこれをやらなければ先には進めません。I'll never go back. 遠慮せずにやっちまいましょう。
するとAre you sure you want to eject? This action is permanent. [y/N]と聞かれるのでもちろん y と入力してエンターを叩きます。

僕のWindows環境では最後にバッチ ファイルが見つかりません。と表示されてエラーになりますが気にしなくてOKです。

TypeScriptをインストール

TypeScriptとか必要そうなものをインストールします。とりあえず最低限これぐらいは入れておいた方がいいでしょう。

$ yarn add --dev typescript tslint awesome-typescript-loader @types/react @types/react-dom @types/node

(VSCodeのみ)

僕はVSCodeしか使わないので他のエディタの場合はどうするのか知りませんが、VSCodeの場合はプロジェクトのワークスペース設定に下記のように書く必要があります。

.vscode/settings.json
{
  "typescript.tsdk": "./node_modules/typescript/lib"
}

これによりTypeScriptのエンジンとしてプロジェクトのnode_modulesフォルダにインストールされたものが使用されるようになります。

tsconfig.jsonを作成する

プロジェクトのルートフォルダで下記のコマンドを実行します。

$ tsc --init

するとこういうファイルが作成されますね。

tsconfig.json
{
    "compilerOptions": {
        "module": "commonjs",
        "target": "es5",
        "noImplicitAny": false,
        "sourceMap": false
    }
}

それを僕ならこんな感じに書き直します。

tsconfig.json
{
  "compilerOptions": {
    "outDir": ".dest-tsc",
    "module": "commonjs",
    "moduleResolution": "node",
    "target": "es5",
    "jsx": "react",
    "lib": [
      "es2017",
      "dom"
    ],
    "typeRoots": [
      "node_modules/@types"
    ]
  },
  "include": [
    "src/**/*"
  ]
}

ついでにsrcフォルダにもtsconfig.jsonを作成します。
TypeScript 2.1からはextendsを使って別のconfigを継承できるので試してみましょう。

src/tsconfig.json
{
  "extends": "../tsconfig.json",
  "compilerOptions": {
    "sourceMap": true,
    "strictNullChecks": true,
    "skipLibCheck": true
  }
}

これでルートのtsconfig.jsonを継承して新しいconfigが作れました。実はこの2つのファイルは目的が違います。

  • /tsconfig.json ... Webpackでバンドルするときに読み込まれるバンドル用の設定。というのもあるけどルートにtsconfig.jsonがないと何かと躓くことが多い。
  • /src/tsconfig.json ... エディタやtslintが読み込んでリアルタイムに型チェックするエディタ用の設定。

僕は主に前者はnullチェックを入れずに、後者にはnullチェックを入れます。

tslint.jsonを作成する

プロジェクトのルートフォルダで下記のコマンドを実行します。

$ tslint --init

するとtslint.jsonが作成されます。

そのままだと一部使い勝手が良くないので少々変更しましょう。
下記の部分を...

tslint.json
    "quotemark": [
      true,
      "double"
    ],
    "semicolon": [
      true,
      "always"
    ],

下記のように書き換えます。

tsling.json
    "quotemark": [
      true,
      "single",
      "jsx-double"
    ],
    "semicolon": [
      false,
      "always"
    ],

jsRulesrulesの2セクションに分かれているので両方とも変更します。

その結果こうなります。

  • 文字列はシングルクォーテーションで囲み、JSXの中だけはダブルクォーテーションで囲む。
  • セミコロンは省略できる。

このへんは個人の好みがあるので各自好きなように設定しましょう。

動作確認してみる

TypeScript環境化する前に一度アプリを動かしてみましょう。

$ npm start

これでブラウザが起動して「Welcome to React」が表示されればOKです。

さて今はJSで動いていますが、これからTS化の作業をしていきます。

Webpackの設定を書き換える

まずconfig/paths.jsの下記の行を

config/paths.js
  appIndexJs: resolveApp('src/index.js'),

下記のように変更します。

config/paths.js
  appIndexJs: resolveApp('src/index.tsx'),

次にconfig/webpack.config.dev.jsです。変更箇所が多いのでテンポ良くいきましょう。

Before

config/webpack.config.dev.js
    extensions: ['.js', '.json', '.jsx', ''],

After

config/webpack.config.dev.js
    extensions: ['.ts', '.tsx', '.js', '.json', '.jsx', ''],

Before

config/webpack.config.dev.js
      {
        exclude: [
          /\.html$/,
          /\.(js|jsx)$/,
          /\.css$/,
          /\.json$/,
          /\.svg$/
        ],
        loader: 'url',
        query: {
          limit: 10000,
          name: 'static/media/[name].[hash:8].[ext]'
        }
      },

After

config/webpack.config.dev.js
      {
        exclude: [
          /\.html$/,
          /\.(js|jsx)$/,
          /\.css$/,
          /\.json$/,
          /\.svg$/,
          /\.tsx?$/,
        ],
        loader: 'url',
        query: {
          limit: 10000,
          name: 'static/media/[name].[hash:8].[ext]'
        }
      },

Before

config/webpack.config.dev.js
      {
        test: /\.svg$/,
        loader: 'file',
        query: {
          name: 'static/media/[name].[hash:8].[ext]'
        }
      }

After

config/webpack.config.dev.js
      {
        test: /\.svg$/,
        loader: 'file',
        query: {
          name: 'static/media/[name].[hash:8].[ext]'
        }
      },
      {
        test: /\.tsx?$/,
        loader: 'awesome-typescript-loader',
      }

ここまでです。config/webpack.config.prod.jsも同じように変更しておくと良いですね。ここでは省略します。

index.js --> index.tsx

いよいよ大詰めです。

srcフォルダのindex.jsindex.tsxに変えましょう。拡張子を変えるだけです。
少しソースコードも書き換えましょう。

Before

src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import './index.css';

ReactDOM.render(
  <App />,
  document.getElementById('root')
);

After

src/index.tsx
import * as React from 'react'; // 変更
import * as ReactDOM from 'react-dom'; // 変更
import App from './App';
import './index.css';

ReactDOM.render(
  <App />,
  document.getElementById('root')
);

App.js --> App.tsx

App.jsApp.tsxのように拡張子を変更します。
ソースコードも一部変更します。

Before

src/App.js
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class App extends Component {
  render() {
    return (
      <div className="App">
        <div className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h2>Welcome to React</h2>
        </div>
        <p className="App-intro">
          To get started, edit <code>src/App.js</code> and save to reload.
        </p>
      </div>
    );
  }
}

export default App;

After

src/App.tsx
import * as React from 'react'; // 変更
const logo = require('./logo.svg'); // 変更
import './App.css';

class App extends React.Component<{}, {}> { // 変更
  render() {
    return (
      <div className="App">
        <div className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h2>Welcome to React</h2>
        </div>
        <p className="App-intro">
          To get started, edit <code>src/App.js</code> and save to reload.
        </p>
      </div>
    );
  }
}

export default App;

動かしてみよう

さあ、これでcreate-react-appで作った環境のTypeScript化が完了しました。
動かしてみましょう。

$ npm start

「Welcome to React」が表示されましたね。You did it!

GitHub Pagesにデプロイする

ここからはGitHub Pagesにデプロイする方法を書きます。デプロイしたい方だけ読んでください。

まずgh-pagesというライブラリをインストールします。

$ yarn add --dev gh-pages

次にpackage.jsonを変更します。

Before

package.json
  "scripts": {
    "start": "node scripts/start.js",
    "build": "node scripts/build.js",
    "test": "node scripts/test.js --env=jsdom"
  },

After

package.json
  "scripts": {
    "start": "node scripts/start.js",
    "build": "node scripts/build.js",
    "test": "node scripts/test.js --env=jsdom",
    "tsc": "tsc",
    "lint": "tslint --type-check \"src/**/*.ts\" \"src/**/*.tsx\" ",
    "deploy": "npm run build && gh-pages -d build"
  },
  "homepage": "https://<your-account-name>.github.io/<your-project-name>",

"tsc""lint"の行は無くても構いません。
"homepage"の行は重要です。account-nameとproject-nameのところをご自身の環境に合わせて変更してください。


これでデプロイできるようになりました。下記のコマンドを実行しましょう。

$ npm run deploy

デプロイが完了したら先程の"homepage": "https://<your-account-name>.github.io/<your-project-name>"で指定したURLをブラウザで開きます。

ちなみに僕がデプロイしたものは こちら です。

お疲れさまでした。

39
31
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
39
31