今日やったこと
- 「モダンJavaScriptの基本から始める React実践の教科書」 P215〜225
- 8-2 APIで取得するデータへの型定義
- 8-3 Propsへの型定義
学習メモ
データの一覧が表示されるアプリをTypeScriptに書き換える
APIを呼ぶと以下のような一覧データが取得できる
例:エンドポイント
https:// example.com/users
例:取得結果
[
{
"id": 1,
"name": "主田",
"age": 24,
"personalColor": "blue"
},
{
"id": 2,
"name": "先岡",
"age": 28,
"personalColor": "pink"
},
{
"id": 3,
"name": "後藤",
"age": 23,
"personalColor": "green"
},
]
TypeScript化後のフォルダ構成
App.tsx
import { useEffect, useState } from "react";
import { ListItem } from "./components/ListItem";
import axios from "axios";
export const App = () => {
// 取得したユーザー情報
const [users, setUsers] = useState([]);
// 画面表示時にユーザー情報取得
useEffect(() => {
axios.get("https://example.com/users").then((res) => {
setUsers(res.data);
})
}, []);
return (
<div>
{users.map(user => (
<ListItem id={user.id} name={user.nama} age={user.age} />
))}
</div>
);
};
ListItem.tsx
export const ListItem = props => {
const { id, name, age} = props;
return (
<p>
{id}:{name}{(age)}
</p>
);
};
- axios はHTTP通信をするためのライブラリで、API通信の際によく使われるもの
- ここではAPIを呼んで、取得データをStateに設定している
上記のコードでは、App.tsxでListItem.tsxにPropsを渡す部分がname
ではなくnama
になってしまっている
{users.map(user => (
<ListItem id={user.id} name={user.nama} age={user.age} />
))}
このような打ち間違いでアプリがおかしくなるケースを、TypeScript使って解決する
取得データへの型定義
「どんなデータが取れるか」というのをあらかじめ型定義しておくことで、フロントエンドのコード内でのバグを減らすことができる
App.tsx(型定義を追加)
import { useEffect, useState } from "react";
import { ListItem } from "./components/ListItem";
import axios from "axios";
// ユーザー情報の型定義
type User = {
id: number;
name: string;
age: number;
personalColor: string;
};
export const App = () => {
// 取得したユーザー情報
const [users, setUsers] = useState<User[]>([]);
// 画面表示時にユーザー情報取得
useEffect(() => {
axios.get<User[]>("https://example.com/users").then((res) => {
setUsers(res.data);
})
}, []);
return (
<div>
{users.map(user => (
<ListItem id={user.id} name={user.nama} age={user.age} />
))}
</div>
);
};
- 今回APIで取得できるのは
id
,name
,age
,personalColor
の4つなのでそれをtypeとしてそれぞれstring、 numberで定義 - axiosの場合は
get<型>
のようにジェネリクスで型を設定するだけでOK- Stateにも同じ型を指定(今回の場合はUserの配列)
この対応により、出力結果にエラーメッセージが表示されるようになり
「いざ本番環境で動かしてみたらおかしかった」という事態を避けることができる
また、型を指定することでエディタで補完が利くようになるため、
例えばuser.
までを入力すると「こういうプロパティがあるよ!」と表示してくれる。
上下カーソルで選択してEnterを押すだけで良いので入力する手間がはぶける
- APIなどは基本的にどんなデータが取れるかわからないので、事前に型を定義しておくことでより安全に開発を進めることができる
- また、バックエンドチームとフロントエンドチームでAPIの認識を合わせる際にも非常に有用
Propsへの型定義
取得データやStateに型を付与することはできたが、現段階ではPropsに対する型定義はされていないので、
以下のようにPropsをnama
に打ち間違えてしまうと先ほどと同じようなバグが起きてしまう
<div>
{users.map(user => (
<ListItem id={user.id} nama={user.name} age={user.age} />
))}
</div>
そのため、Propsに対しても型を定義する
ListItem.tsx
// Propsの型定義
type User = {
id: number;
name: string;
age: number;
}
export const ListItem = (props:User) => {
const { id, name, age} = props;
return (
<p>
{id}:{name}{(age)}
</p>
);
};
このように型を指定すると、先ほどと同様にエラーメッセージが表示されるようになる
また、試しにname
を消してみると定義しているはずの型がPropsに指定されていない場合もエラーが検出されることが分かる
TypeScriptがリファクタリングに強い理由
追加で文字色をpersonalColor
にする修正が入ったため
コンポーネントに新しくPropsを追加する
まずは型定義を修正
ListItem.tsx
// Propsの型定義
type User = {
id: number;
name: string;
age: number;
personalColor: string;
}
必要な型がないというエラーが出る
Props に personalColor
を追加する
ListItem.tsx
// Propsの型定義
type User = {
id: number;
name: string;
age: number;
personalColor: string;
}
export const ListItem = (props:User) => {
const { id, name, age, personalColor } = props;
return (
<p style="{{ color: personalColor }}">
{id}:{name}{(age)}
</p>
);
};
App.tsx
{users.map(user => (
<ListItem id={user.id} name={user.name} age={user.age} personalColor={user.personalColor}/>
))}
このように、機能追加の際にまず型定義から修正していくことで、型定義をしたあとにエラーになった箇所を対応していけば良いので影響範囲がわかりやすくなる
TypeScriptがリファクタリングに強いと言われるのはこのため
所感
TypeScriptの利点について学びました。
一度設定すれば後々楽になるので積極的に使っていきたいです。