メインコンテンツまでスキップ

速習 React × TypeScript

前置き

この章では、TypeScript で React を書く方法を学んでいきます。

JavaScript で React を書いたことがあれば、型指定が少し増えるくらいなので難易度はそこまで高くないでしょう。

「React は多少学習したけど、そこまで自信ない...」という場合でも、解説とサンプルコードを併せて見ていただくことでイメージが掴めるかと思います。

note

もちろん、解説やサンプルコード見聞きするだけでなく、勉強会後に復習の意味も込めて、あなた自身の環境でもコードを書いて動作確認までしてください。

わかったつもりでいたけど、実際に書くとなると何をすれば良いかわからず手が止まってしまう...」というのはよくあることです。

最初はなかなか思うように動かなくても、調べながらコードを改修して意図通りに動かせるようになる積み重ねが大事です。

Vite で React × TypeScript の環境を構築する

「React × TypeScript」の環境を構築する方法はいくつかあります。

今回の勉強会では「Vite」を使って進めていきます。

開発環境

この資料作成時点での僕の開発環境は以下の通りです。(2022 年 7 月 24 日現在)

  • OS : macOS Monterey(バージョン 12.3)
  • Node.js : v16.16.0
  • Web ブラウザ : Google Chrome(バージョン: 103.0.5060.134(Official Build) (x86_64))

僕は Mac を使っていますが、Windows, Linux でも Node.js は使えるので、利用している OS に合わせて Node.js をインストールしていただけたらと思います。

必須ではありませんが、Git でコード管理できるようにしておくこともおすすめします。

Git を活用して、作成したアプリを無料でインターネット公開することもできます。(詳細は以下を参照)

info

Git でコードを管理すると、GitHub にアップできるようになります。

GitHub にコードをアップすると、以下のようなサービスと連携することで、開発したアプリを無料でインターネットに公開もできます。

例えば、今回の勉強会用に GitHub に用意したサンプルコードは Vercel と連携しているので、サンプルコードで実装したアプリは無料でインターネットに公開しています。

公開しているアプリはこちらから

また、この学習サイトも「コードは GitHub で管理」「GitHub と Vercel を連携してインターネット公開」という形で管理しています。

Vite を使ってプロジェクトを作る

Vite ドキュメントの「最初の Vite プロジェクトを生成する」の指示に従って、インストール作業を進めます。

以下の動画でプロジェクト作成の流れを解説しているので、こちらも参考にどうぞ。

React Developer Tools をブラウザに追加していない人向け

React には、開発を便利にしてくれる「React Developer Tools」というブラウザ拡張機能があります。

こちらを使うと、以下のようなことがデベロッパーツールで確認できるようになります。

  • コンポーネントのツリー構造
  • コンポーネントに渡された props の内容
  • コンポーネントが持つ state の値

React アプリのデバッグがしやすくなるので、こちらの拡張機能を追加しておくことをおすすめします。

https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=ja

プロジェクトの中身を確認する

Vite でプロジェクトを作成した後は、普段使っているエディタでプロジェクトを開いてください。

以下の画像は Visual Studio Code でプロジェクトを開いた時のディレクトリ構造をキャプチャしたものです。

初期状態のディレクトリ構造

色々とファイルがありますが、ここでは拡張子が .tsx のものに注目してください。(画像内の赤枠で囲っている部分

JavaScript で React コードを書く時、拡張子が .jsx もしくは .js のファイルに作成します。

それに対して TypeScript で React コードを書くときは、 .tsx もしくは .ts でファイルを作成します。

info

React を使わない場合でも、TypeScript ファイルの拡張子は .ts となります。(JavaScript ファイルの拡張子が kakuchoushiga .js であるのと同じように)

React × TypeScript のまずはこれを覚えておこう 4 選

React × TypeScript の細かい部分まで取り上げると説明がダラダラと長くなるので、ここでは、僕の独断と偏見で「React × TypeScript で見かけることが多い」と感じる書き方や作法を 4 つ解説していきます。

注意

ここで取り上げるのはほんの一部です。ここで取り上げてない書き方や作法を見かけた場合は、繰り返しになりますが都度ググりましょう。(調べる癖をつける)

1. useState の型を指定する

結論から書くと、TypeScript で useState を書くときは以下の 2 通りがあります。

TypeScriptでのuseStateの書き方
// - JavaScriptと同じような書き方// - 初期値としてセットした値の型が暗黙的に推論される(以下の例だと0なので、number型となる)// - setCount('string') のように異なる型をセットしようとするとエラーになるconst [count, setCount] = useState(0);// - 明示的に型を指定する書き方// - 以下のコードはエラーになる(string型だと指定しているのに、初期値にnumber型の0をセットしているため)const [nickname, setNickname] = useState<string>(0);// - 以下のコードの場合、nameにはstring型 もしくは undefined型が許可される(初期値をセットしていない場合、undefined型も許可されるようになる)const [lastName, setLastName] = useState<string>();// - 以下のコードの場合、nameにはstring型のみが許可される(初期値にもstring型をセットしている)const [firstName, setFirstName] = useState<string>('');

2. コンポーネントの Props の型を指定する

以下のコードは Appコンポーネント の子コンポーネントとして UserProfileコンポーネント をセットしたコードとなります。

ハイライト箇所に注目(1行目, 27-29行目, 31行目)
import type { FC } from 'react';type User = {  nickname: string;  age: number;};function App() {  const userA: User = {    nickname: 'Taro',    age: 25,  };  const userB: User = {    nickname: 'Hanako',    age: 24,  };  return (    <div>      <UserProfile user={userA} />      <UserProfile user={userB} />    </div>  );}type Props = {  user: User;};const UserProfile: FC<Props> = ({ user }) => {  return (    <div>      <div>名前 : {user.nickname}</div>      <div>年齢 : {user.age}</div>    </div>  );};

App コンポーネントと UserProfile コンポーネントの親子関係

ハイライト箇所の説明

上のコードのハイライト箇所でやっていることは、それぞれ以下の通りです。

  • 1 行目で、関数コンポーネントの型「FC」を import している(おそらく Functional Component(=関数コンポーネント)の頭文字をとったもの)
  • 27-29 行目で、31 行目に実装する関数コンポーネントの props の型を作成している
  • 31 行目で、1 行目の FC, 27-29 行目の Props を使って、関数コンポーネントの受け取れる props の型を指定している
note

1 行目の import type については後ほど解説します。

少し面倒に思われるかもしれませんが、このような方法でコンポーネントの props の型を定義することで、TypeScript の恩恵を受けることができます。

恩恵の具体例は以下の画像をご覧ください。

不足している props を教えてくれる(エラー表示)

不足している props を教えてくれる(エラー表示)

props の型の違いを教えてくれる(エラー表示)

props の型の違いを教えてくれる(エラー表示)

指定できる props の key(プロパティ名)と、対応する型を補完機能で教えてくれる

指定できる props の key(プロパティ名)と、対応する型を補完機能で教えてくれる

上の画像だけでも TypeScript の恩恵は伝わりましたでしょうか?

TypeScript を使うことで、アプリの動作確認を行う前に問題箇所を事前に知ることができます。

毎回動作確認をしなくても、「props 不足」「引数不足」「型違い」を事前に気付けるようになるので、開発効率が格段にあがります。

3. 外部ライブラリ用の型ファイルの利用する(@types のインストール)

以下の画像コードは npm i uuid を実行して、uuid というライブラリをインストールして、ドキュメントに従って uuid を import した場面となります。

画像を見ていただくと分かる通り、1 行目の uuid の下の部分が赤の波線になっていてエラーとなっています。

uuidのimportでエラーになっている様子

note

uuid については、GitHub レポジトリや npmjs.com を参照してください。

エラーメッセージの内容を読んでみると、エラーメッセージの上から 2 行目に Could not find a declaration file for module 'uuid'. と書かれているのがわかります。

このメッセージから、import して使おうとしているライブラリ(今回で言うとuuid)の宣言ファイルが見つからないというのがわかります。

ここでいう宣言ファイルというのは、型が定義されたファイルだと認識してもらって問題ありません。

この問題を解決するためには uuid の型を定義した宣言ファイルを作る必要があります。

自分でゼロから作ると言うのも 1 つの手ですが、有名どころのライブラリは「DefinitelyTyped」というところで、宣言ファイルが用意されていることが多いです。

なので、まずは用意されている場合はそれを利用することから考えましょう。

利用方法は簡単で、上の画像の赤枠部分でも書かれているように「npm i --save-dev @types/ライブラリ名」を実行するだけです。

今回の例でいえば「npm i --save-dev @types/uuid」となります。

もし「DefinitelyTyped」に対象とする宣言ファイルが存在していれば、宣言ファイルのインストールが完了して、先ほどのエラーは解決します。

以下の画像は、「npm i --save-dev @types/uuid」を実行した後の様子を撮ったものです。(エラーがなくなっているのがわかるかと思います。)

uuidのimportエラーが解決した様子

caution

DefinitelyTyped」には、有名なライブラリの宣言ファイルが用意されていることが「多い」と話しましたが、ライブラリによっては宣言ファイルが用意されていない場合もあります。

その場合は、以下の手段を取る必要があります。

  • 自分で宣言ファイルを作る
  • 他の似たようなライブラリを探す
info

今回の説明で紹介した uuid には、ライブラリ内(パッケージ内)に宣言ファイルが用意されていませんが、最初から宣言ファイルが用意されているライブラリも存在します。

例えば axios がその 1 つです。(axiosは Promise ベースで HTTP 通信を行うことができる有名なライブラリの 1 つ)

4. as キーワードを使う(型アサーション)

as キーワード、型を上書きするものだと認識してもらって問題ありません。(as キーワードを「型アサーション」と呼んだりもする)

以下のサンプルコードの 18-20 行目では、「API 通信をしてデータを取得 → 取得したデータを state にセット」しています。

19 行目で as を使っていますが、ここで response.data を any 型から Post の配列型に上書きしています。

19行目でasを使っている
import { useState, useEffect } from 'react';import axios from 'axios';const API_URL = 'https://jsonplaceholder.typicode.com/posts';type Post = {  id: number;  userId: number;  title: string;  body: string;};function App() {  const [posts, setPosts] = useState<Post[]>([]);  useEffect(() => {    (async () => {      const response = await axios.get(API_URL);      const data = response.data as Post[];      setPosts(data);    })();  }, []);  return (    <table border={1}>      <thead>        <tr>          <th>id</th>          <th>userId</th>          <th>title</th>          <th>body</th>        </tr>      </thead>      <tbody>        {posts.map((post) => {          const { id, userId, title, body } = post;          return (            <tr key={id}>              <td>{id}</td>              <td>{userId}</td>              <td>{title}</td>              <td>{body}</td>            </tr>          );        })}      </tbody>    </table>  );}export default App;

as は型の元々セットされてた型の上書きとなるので、基本的に as を使わないで対応できるならそれにこしたことはありません。

しかし、as は便利な面もあるので、TypeScript のコードを読むとしばしば表れます。

そのため、as は早い段階で覚えておくと良いかなと思い、4 選の最後の 1 つとして紹介しました。

info

ここでは「型アサーション(as)」 についてざっくりとした解説をしました。

さらに詳しく「型アサーション(as)」以下のサイトを参考にすると良いかなと思います。

React の参考資料

React に全く触れたことがない方は、上記の説明中、React 特有の箇所でわからなかった部分もあったかと思います。

React をイメージをざっくりとでも掴めるようになりたい場合は、公式ドキュメントの Docsの中にある「MAIN CONCEPTS」と「HOOKS」の部分を一読すると良いでしょう。

tip

MAIN CONCEPTS の中には、クラスコンポーネントの解説もありますが、「最短でとりあえず今どきの React を知りたい」という場合は「関数コンポーネントの理解」「HOOKS」の理解をしておけば良いです。

特に HOOKS では「useState」「useEffect」の 2 つは押さえておきましょう。(この 2 つは他の Hooks の機能と比べて、特に使うことが多い Hooks になります)

余裕があれば Context(useContext)も覚えておくと良いです。