ReactのプロジェクトにTypeScriptを導入する〜npm installからコンパイルまで〜

ReactのプロジェクトにTypeScriptを導入する〜npm installからコンパイルまで〜:


はじめに

TypeScript Advent Calendarの初日ということで、TypeScriptの導入についての記事を書いてみました。
主に「いまReactを使っていて、TypeScriptも使ってみたい!」という方に向けた記事です。

最初にTypeScriptについて触れておくと、

TypeScriptは「型を持ったJavaScriptのスーパーセットです」

TypeScriptの公式サイト
TypeScriptのリポジトリ

  • Microsoftによって開発され、メンテナンスされているオープンソースのプログラミング言語
  • クラスベースのオブジェクト指向
  • 型情報をもとに実行前にエラーを検出でき、コンパイルする事でJavaScriptのコードにトランスパイルされる
  • ES2015移行で書かれたコードをES5にトランスパイルできる
  • JSXをサポートしているので、Reactとも併用できる
という特徴を持っています。

Google Trendsを見ても、その人気が分かると思います。


typescript-trends.png

TypeScriptのトレンド動向

それではReactのプロジェクトにTypeScriptを導入する流れを紹介します。


導入するReactのプロジェクト

今回、Reactで書かれたカウンターアプリをサンプルとして使います。

サンプルプロジェクトのリポジトリはこちらを参照ください。

プロジェクトの構成は以下の通りです。

  • index.jsを起点にしたReactのコンポーネント
  • トランスパイルやコンパイルはwebpack 4 + Babel 7
$ tree                                                                                                                                               (master) 
. 
├── dist 
│   ├── bundle.js 
│   └── index.html 
├── package.json 
├── .babelrc 
├── src 
│   ├── index.html 
│   ├── index.js 
│   └── js 
│       └── components 
│           ├── container 
│           │   └── CounterContainer.js 
│           └── presentational 
│               └── Button.js 
└── webpack.config.js 


TypeScriptの導入

それでは紹介したカウンターアプリにTypeScriptを導入します。

※ 新規でTypeScriptの環境を構築する場合はcreate-react-appで簡単に行えます。

今回変更した内容はこのプルリクエストで確認できます。

TypeScriptを用いたReactのファイルは拡張子が.tsxになります。

なので、今回は「Reactのファイルが全て.tsxになり、コンパイルが通る」ことがゴールです。


1. TypeScriptのインストール

まずはTypeScriptに必要なライブラリをインストールします。

$ npm i typescript ts-loader --save-dev 
ts-loaderは、webpackの中でTypeScriptで書かれたファイルをjsファイルに変換するためにインストールします。


2. webpackの設定(webpack.config.js)

webpackの設定をTypeScript(tsx)用に変更します。

webpack.config.jsの変更内容は以下の通りです。

module.exports = { 
- entry: './src/index.js', 
+ entry: './src/index.tsx', 
  ... 
  resolve: { 
-   extensions: ['*', '.js', '.jsx'] 
+   extensions: ['.ts', '.tsx', '.js'] 
  }, 
  module: { 
    rules: [ 
      { 
-       test: /\.(js|jsx)$/, 
+       test: /\.tsx?$/, 
        exclude: /node_modules/, 
        use: { 
-         loader: "babel-loader" 
+         loader: "ts-loader" 
        } 
  ... 
}; 
entryや拡張子、そして先程インストールしたts-loaderにloaderを変更します。


3. TypeScriptの設定(tsconfig.json)

次にTypeScriptのコンパイルに必要な設定ファイルとしてtsconfig.jsonを追加します。

$ ./node_modules/.bin/tsc --init 
.tsconfig.jsonでjsxの有効化やトランスパイルのターゲット、出力するモジュール方式などを設定します。
noUnusedLocalsnoUnusedParametersなど自由に設定してください。
tsconfig.jsonの各種プロパティはこちらのTypeScript HandBook tsconfig.jsonを参照してください。

今回は最小限以下のように設定しました。

{ 
  "compilerOptions": { 
    "outDir": "./dist/", 
    "sourceMap": true, 
    "noImplicitAny": true, 
    "module": "ES2015", 
    "target": "es5", 
    "jsx": "react" 
  } 
} 


4. 型定義ファイル@types/のインストール

TypeScriptではnpmパッケージとして型定義ファイルが提供されています。(TypeScript2.0系から)

Reactなどのnpmライブラリを使う場合、型情報として@types/xxxを一緒にインストールします。

またReduxのようにパッケージに型定義が同梱されている場合はインストールは不要です。

ReactでTypeScriptを利用する場合、Reactの型定義ファイル(@types/react@types/react-dom)をインストールする必要があります。

$ npm i @types/{react,react-dom} --save-dev 


5. 不要になったファイルやライブラリを削除

TypeScriptを導入したことで、不要になったファイルやライブラリを削除します。

ES5へのトランスパイルやjsx記法のコンパイルはTypeScriptで行うことができるのでBabelが不要になります。

また、propsの型情報はTypeScriptで定義するのでprop-typesも不要になります。

$ npm remove @babel/core @babel/preset-env @babel/preset-react babel/loader 
$ npm remove prop-types 
$ rm .babelrc 


6. Reactコンポーネントのファイルに型を書く(js -> tsx)

最後にReactコンポーネントに型を導入して、コンパイルを行います。

まずは拡張子を.jsから.tsxにリネームしましょう。

$ git mv src/index.js src/index.tsx 
$ git mv src/js/components/container/CounterContainer.js src/js/components/container/CounterContainer.tsx 
$ git mv src/js/components/presentational/Button.js src/js/components/presentational/Button.tsx 
あとはファイルの中身を書き換えていきます。

index.tsxの変更点は以下の通りです。

- import React, { Component } from "react"; 
- import ReactDOM from "react-dom"; 
+ import * as React from 'react'; 
+ import * as ReactDOM from 'react-dom'; 
 
import CounterContainer from "./js/components/container/CounterContainer"; 
 
const rootEl = document.getElementById('root') 
rootEl ? ReactDOM.render(<CounterContainer />, rootEl) : false; 
ここでは、reactract-domのimportの書き方が変わります。

次にCounterContainer.tsxを修正します。

変更点は以下の通りです。

- import React, { Component } from "react"; 
+ import * as React from 'react'; 
  import Button from "../presentational/Button"; 
 
- class CounterContainer extends Component { 
-   constructor() { 
-     super(); 
-     this.state = { 
-       count: 0 
-     } 
-     this.countUp = this.countUp.bind(this) 
-   } 
-   countUp() { 
 
+ interface Props {} 
 
+ interface State { 
+   count: number; 
+ } 
 
+ export default class CounterContainer extends React.Component<Props, State> { 
 
+ public state: State = { 
+   count: 0 
+ }; 
 
+ public countUp = () => { 
    this.setState({count: this.state.count + 1}) 
  } 
 
  render() { 
    return ( 
      <div> 
        <div>count:{this.state.count}</div> 
        <Button label="count up!" onClick={this.countUp}/> 
      </div> 
    ) 
  } 
} 
- export default CounterContainer; 
class Componentの定義はReact.Component<P, S>をextendsして作成します。
Pがpropsの型、Sがstateの型となります。

最後にButton.tsxを修正します。

変更点は以下の通りです。

- import React from "react"; 
- import PropTypes from "prop-types"; 
+ import * as React from 'react'; 
 
+ interface Props { 
+   label: string; 
+   onClick: () => void; 
+ } 
 
- const Button = ({ label, onClick }) => ( 
+ const Button = (props: Props) => ( 
-   <button onClick={onClick}>{label}</button> 
+   <button onClick={props.onClick}>{props.label}</button> 
  ); 
 
- Button.propTypes = { 
-   label: PropTypes.string.isRequired, 
-   onClick: PropTypes.func.isRequired 
- }; 
-  
  export default Button; 
propTypesではなく、TypeScriptでpropsの型を定義します。

これでtsxファイルの修正は終わりです。


7. tsxファイルをコンパイルする

最後に、これでコンパイルが通ることを確認します。

$ npm run build 
> webpack --mode production 
 
Hash: 18d8fcfcb0ded510ffa2 
Version: webpack 4.26.1 
Time: 1776ms 
Built at: 2018-11-30 21:14:11 
       Asset       Size  Chunks             Chunk Names 
./index.html  281 bytes          [emitted] 
   bundle.js    109 KiB       0  [emitted]  main 
Entrypoint main = bundle.js 
[7] ./src/index.tsx + 2 modules 1.88 KiB {0} [built] 
    | ./src/index.tsx 279 bytes [built] 
    |     + 2 hidden modules 
    + 7 hidden modules 
Child html-webpack-plugin for "index.html": 
     1 asset 
    Entrypoint undefined = ./index.html 
    [0] ./node_modules/html-webpack-plugin/lib/loader.js!./src/index.html 265 bytes {0} [built] 
無事、コンパイルすることができました!

コンパイル済みのファイルはdist/bundle.jsに出力され、dist/index.htmlから読み込まれます。

最終的な構成は以下のようになりました。

$ tree 
. 
├── dist 
│   ├── bundle.js 
│   └── index.html 
├── package.json 
├── src 
│   ├── index.html 
│   ├── index.tsx 
│   └── js 
│       └── components 
│           ├── container 
│           │   └── CounterContainer.tsx 
│           └── presentational 
│               └── Button.tsx 
├── tsconfig.json 
└── webpack.config.js 


参考記事

コメント

このブログの人気の投稿

投稿時間:2021-06-17 05:05:34 RSSフィード2021-06-17 05:00 分まとめ(1274件)

投稿時間:2021-06-20 02:06:12 RSSフィード2021-06-20 02:00 分まとめ(3871件)

投稿時間:2020-12-01 09:41:49 RSSフィード2020-12-01 09:00 分まとめ(69件)