🦔
create-react-appを使わないReactのTypeScript化
はじめに
前回create-react-appを使わずにReactの環境構築を行なった。
上記ではJavaScriptしか使えないため、TypeScriptを使うように修正していく。
なおTypeScriptからJavaScriptへのトランスパイルに関しては、babelではなくtscのみで行う。
成果物はこちら。
手順
Babelのアンインストール
前記事ではbabelを使用していたが、今回は使用しないためBabel関連パッケージをすべてアンインストールする。
$ yarn remove @babel/cli @babel/core @babel/preset-env @babel/preset-react babel-loader
TypeScriptのインストール
TypeScriptを使用できるよう、Webpackの関連パッケージも含めてインストールする
$ yarn add --dev typescript ts-loader @types/react @types/react-dom
- typescript
- TypeScriptを使用するためのパッケージ
- https://github.com/microsoft/TypeScript
- ts-loader
- WebpackのTypeScriptを使用するためのプラグイン
- https://github.com/TypeStrong/ts-loader
- @types/react
- reactの型定義情報が含められたパッケージ
- https://www.npmjs.com/package/@types/react
- @types/react-dom
- react-domの型定義情報が含められたパッケージ
- https://www.npmjs.com/package/@types/react-dom
TypeScriptの設定ファイルの作成
プロジェクト直下に tsconfig.json
を作成する。
tsconfig.json
{
"compilerOptions": {
"target": "es2015",
"module": "esnext",
"outDir": "./dist/",
"esModuleInterop": true,
"moduleResolution": "node",
"jsx": "react",
"allowJs": true,
"strict": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
},
}
- target
- TypeScriptをコンパイルして出力されるJavaScriptをどのバージョンで出力するか
- https://www.typescriptlang.org/tsconfig#target
- module
- 出力されるJavaScriptがどのようにモジュールを読み込むか
- https://www.typescriptlang.org/tsconfig#module
- outDir
- トランスパイル後のファイルの出力先
- https://www.typescriptlang.org/tsconfig#outDir
- esModuleInterop
- CommonJSモジュールをESModulesのimportで読み込めるようにするか
- https://www.typescriptlang.org/tsconfig#esModuleInterop
- moduleResolution
- import対象のファイルをどのように検索するか
- https://www.typescriptlang.org/tsconfig#moduleResolution
- jsx
- トランスパイル時にJSXコンストラクタをどの方法で制御するか
- https://www.typescriptlang.org/tsconfig#jsx
- allowJs
- tsファイルの中でjsファイルをimportできるようにするか
- https://www.typescriptlang.org/tsconfig#allowJs
- strict
- 以下の型チェックオプションを全て有効にするか
- alwaysStrict
- トランスパイル後のファイルすべてに
use strict
が出力される - https://www.typescriptlang.org/tsconfig#alwaysStrict
- トランスパイル後のファイルすべてに
- strictNullChecks
- nullとundefinedを厳密にチェックする
- https://www.typescriptlang.org/tsconfig#strictNullChecks
- strictBindCallApply
-
call
,bind
,apply
メソッド実行時の引数の型を厳密にチェックする - https://www.typescriptlang.org/tsconfig#strictBindCallApply
-
- strictFunctionTypes
- 関数の型を厳密にチェックする
- https://www.typescriptlang.org/tsconfig#strictFunctionTypes
- strictPropertyInitialization
- クラス変数が初期値が代入されているか
- https://www.typescriptlang.org/tsconfig#strictPropertyInitialization
- noImplicitAny
- 型注釈がない場合にany型として扱われないようにする
- https://www.typescriptlang.org/tsconfig#noImplicitAny - noImplicitThis
- thisの型注釈を必須にする
- https://www.typescriptlang.org/tsconfig#noImplicitThis
- useUnknownInCatchVariables
- catchした変数をany型からunknows型にする
- https://www.typescriptlang.org/tsconfig#useUnknownInCatchVariables
- alwaysStrict
- https://www.typescriptlang.org/tsconfig#strict
- 以下の型チェックオプションを全て有効にするか
- skipLibCheck
- 外部ライブラリの型チェックをスキップするか(トランスパイルの速度が上がるが、型チェックが厳密ではなくなる)
- https://www.typescriptlang.org/tsconfig#skipLibCheck
- forceConsistentCasingInFileNames
- ファイル名を大文字と小文字で一貫させるか
- https://www.typescriptlang.org/tsconfig#forceConsistentCasingInFileNames
Webpackの設定ファイルの修正
webpack.config.js を下記のように修正する。
webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
module.exports = {
- entry: './index.js',
+ entry: './index.tsx',
mode: 'development',
output: {
path: path.resolve(__dirname, './dist'),
filename: 'bundle.js',
},
target: 'web',
devServer: {
port: '3000',
static: {
directory: path.join(__dirname, 'public'),
},
open: true,
hot: true,
liveReload: true,
},
resolve: {
- extensions: ['.js', '.jsx'],
+ extensions: ['.tsx', '.ts', '.js'],
},
module: {
rules: [
{
- test: /\.(js|jsx)$/,
+ test: /\.(js|ts|tsx)$/,
exclude: /node_modules/,
- use: 'babel-loader',
+ use: 'ts-loader',
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname, 'public', 'index.html'),
}),
],
};
ファイル拡張子の修正
ファイルをそれぞれ下記のようにリネームする。
-
./index.js
→./index.tsx
-
./src/App.js
→./src/App.tsx
ファイル内容の修正
ファイルをそれぞれ下記のように修正する。
./index.tsx
import React from 'react';
import { createRoot } from 'react-dom/client';
- import App from './src/App.js';
+ import App from './src/App';
- const container = document.getElementById('root');
- const root = createRoot(container);
+ const root = createRoot(document.getElementById('root') as HTMLElement);
+ root.render(
+ <React.StrictMode>
+ <App />
+ </React.StrictMode>
+ );
./src/App.tsx
import React from 'react';
+ import type { FC } from 'react';
- const App = () => {
+ const App: FC = () => {
return <h1>Hello, world!</h1>;
};
export default App;
実行
CLIで下記のコマンドを実行すると、ブラウザにページが表示される。
$ yarn start
おわりに
TypeScript化ができたので、次はEslintの導入をする。
参考
メモ
最小限のtsconfig.json
これだけあればエラーが発生せずに yarn start
の実行はできる。
tsconfig.json
{
"compilerOptions": {
"esModuleInterop": true,
"jsx": "react",
},
}
エラー
Module not found Error: Can't resolve '.'
発生タイミング
下記コマンド実行時。
$ yarn start
→ webpack-dev-server . が実行される
解決方法
-
webpack.config.js
のentry
に指定しているファイルのパスが、package.json
のscripts
にしているwebpack-dev-server ."
に指定しているディレクトリに存在に適切にビルドされた
疑問
-
webpack.config.js
のentry
でビルドするエントリのファイル指定してるのに、webpackコマンド実行時に指定するパスがentryで指定しているファイルと同じディレクトリに存在している必要があるの?
Discussion
js指定しなくても動く?
tsconfig.jsonのskipLibCheckはfalseがよい?