ReactのプロジェクトにTypeScriptを導入する〜npm installからコンパイルまで〜
ReactのプロジェクトにTypeScriptを導入する〜npm installからコンパイルまで〜:
TypeScript Advent Calendarの初日ということで、TypeScriptの導入についての記事を書いてみました。
主に「いまReactを使っていて、TypeScriptも使ってみたい!」という方に向けた記事です。
最初にTypeScriptについて触れておくと、
TypeScriptは「型を持ったJavaScriptのスーパーセットです」
TypeScriptの公式サイト
TypeScriptのリポジトリ
Google Trendsを見ても、その人気が分かると思います。
TypeScriptのトレンド動向
それではReactのプロジェクトにTypeScriptを導入する流れを紹介します。
今回、Reactで書かれたカウンターアプリをサンプルとして使います。
サンプルプロジェクトのリポジトリはこちらを参照ください。
プロジェクトの構成は以下の通りです。
それでは紹介したカウンターアプリにTypeScriptを導入します。
※ 新規でTypeScriptの環境を構築する場合はcreate-react-appで簡単に行えます。
今回変更した内容はこのプルリクエストで確認できます。
TypeScriptを用いたReactのファイルは拡張子が
なので、今回は「Reactのファイルが全て
まずはTypeScriptに必要なライブラリをインストールします。
ts-loaderは、webpackの中でTypeScriptで書かれたファイルをjsファイルに変換するためにインストールします。
webpackの設定をTypeScript(tsx)用に変更します。
webpack.config.jsの変更内容は以下の通りです。
entryや拡張子、そして先程インストールした
次にTypeScriptのコンパイルに必要な設定ファイルとして
今回は最小限以下のように設定しました。
TypeScriptではnpmパッケージとして型定義ファイルが提供されています。(TypeScript2.0系から)
Reactなどのnpmライブラリを使う場合、型情報として@types/xxxを一緒にインストールします。
またReduxのようにパッケージに型定義が同梱されている場合はインストールは不要です。
ReactでTypeScriptを利用する場合、Reactの型定義ファイル(
TypeScriptを導入したことで、不要になったファイルやライブラリを削除します。
ES5へのトランスパイルやjsx記法のコンパイルはTypeScriptで行うことができるので
また、propsの型情報はTypeScriptで定義するので
最後にReactコンポーネントに型を導入して、コンパイルを行います。
まずは拡張子を
あとはファイルの中身を書き換えていきます。
ここでは、
次に
変更点は以下の通りです。
class Componentの定義は
最後に
変更点は以下の通りです。
propTypesではなく、TypeScriptでpropsの型を定義します。
これで
最後に、これでコンパイルが通ることを確認します。
無事、コンパイルすることができました!
コンパイル済みのファイルは
最終的な構成は以下のようになりました。
はじめに
TypeScript Advent Calendarの初日ということで、TypeScriptの導入についての記事を書いてみました。主に「いまReactを使っていて、TypeScriptも使ってみたい!」という方に向けた記事です。
最初にTypeScriptについて触れておくと、
TypeScriptは「型を持ったJavaScriptのスーパーセットです」
TypeScriptの公式サイト
TypeScriptのリポジトリ
- Microsoftによって開発され、メンテナンスされているオープンソースのプログラミング言語
- クラスベースのオブジェクト指向
- 型情報をもとに実行前にエラーを検出でき、コンパイルする事でJavaScriptのコードにトランスパイルされる
- ES2015移行で書かれたコードをES5にトランスパイルできる
- JSXをサポートしているので、Reactとも併用できる
Google Trendsを見ても、その人気が分かると思います。
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
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" } ... };
ts-loader
にloaderを変更します。
3. TypeScriptの設定(tsconfig.json)
次にTypeScriptのコンパイルに必要な設定ファイルとしてtsconfig.json
を追加します。$ ./node_modules/.bin/tsc --init
.tsconfig.json
でjsxの有効化やトランスパイルのターゲット、出力するモジュール方式などを設定します。noUnusedLocals
やnoUnusedParameters
など自由に設定してください。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;
react
とract-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;
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;
これで
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
コメント
コメントを投稿