
【React×TypeScript】よく使う型定義5パターン入門
この記事は8分で読めます
こんにちは。今日も記事をご覧いただきありがとうございます。
React に TypeScript を導入したての頃、こんな場面で手が止まりませんか。
「props にどう型を付けるんだっけ」
「useState の中身が null になり得るとき、どう書くのが正解?」
「onChange の型、毎回ググってる気がする…」
TypeScript は覚えることが多く見えて、React と組み合わせると「型のバリエーションが無限にありそう」と感じてしまいます。
でも実際に手を動かしてみると、日常的に使う型はごく限られています。
React × TypeScript は、まず5つのパターンだけ押さえれば実務のほとんどをカバーできます。
型は書き手を縛る「敵」ではなく、うっかりミスから守ってくれる味方です。
この記事では、よく使う5パターンを、使う場面とコード例つきで整理していきます。
型はエラーを未然に防ぐ「安全ネット」の役割
型の話をすると「書く量が増えそう」と身構えてしまうかもしれませんが、型の本当の価値はエラーを未然に防ぐことにあります。
たとえば string のみの User props に number の age を追加したとき、TypeScript の型を使っていると使っている側のコンポーネントで即座に赤線が出ます。
JavaScript だと、実行して初めて undefined に気付き、場合によっては本番で気付くこともあります。
型は「未来の自分が壊した箇所を、その場で教えてくれる仕組み」です。
書く量は少し増えますが、後から直す手間は確実に減ります。
それでは、実際のパターンを見ていきましょう。
よく使う5パターン
パターン1: props の型
一番よく使うのが、コンポーネントの props に型を付けるパターンです。基本形は型エイリアス(type)で書けば十分です。
type UserCardProps = {
name: string;
age: number;
bio?: string; // 省略可能
};
export const UserCard = ({ name, age, bio }: UserCardProps) => {
return (
<div>
<h2>{name}({age})</h2>
{bio && <p>{bio}</p>}
</div>
);
};
ポイントは3つです。
-
プロパティ名のあとに
?:を付けると省略可能(Optional)になる。 -
分割代入と組み合わせると、関数内で
props.nameと書かずnameだけで使えて読みやすくなる。 -
typeとinterfaceは基本的にどちらでもOK。迷ったらtypeで統一しておく良い。
パターン2: useState の型
useState は初期値から型を判定してくれます。
const [count, setCount] = useState(0); // number と推論される
const [name, setName] = useState(''); // string と推論される
一方、初期値が null だったり、複雑な型を入れる場合はジェネリクスで明示します。
ジェネリクスとは、関数や型を「どんな型で使うか」を後から指定できる仕組みです。
< > の中に使いたい型を書くことで、その型として振る舞わせられます。
下の例では useState<User | null> と書き、「この状態は User か null が入る」と TypeScript に伝えています。
type User = { id: number; name: string };
const [user, setUser] = useState<User | null>(null);
判断の目安はシンプルです。初期値だけで型が決まるなら書かない。
あとで別の型が入る可能性があるなら明示する。この2つだけ意識すれば迷いません。
チーム開発の場合は型を全て指定しているプロジェクトもありますので、チームの書き方に合わせた方が無難です。
なお、useState を含む Hooks の使いどころを整理したい方は、【React入門】よく使うHooks 5選も参考にしてみてください。
パターン3: イベントハンドラの型
フォームやボタンのハンドラでは、以下のような専用のイベント型を使います。
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setValue(e.target.value);
};
const onClick = (e: React.MouseEvent<HTMLButtonElement>) => {
console.log('clicked');
};
const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
};
JSX の中にインラインで書く場合は型を省略しても推論されます。
共通関数として切り出すときだけ、明示的に書けば十分です。
// インラインなら推論に任せてOK
<input onChange={(e) => setValue(e.target.value)} />
チーム開発の場合はこちらもチームの慣習に合わせましょう。
パターン4: children の型
子要素を受け取るコンポーネントでは React.ReactNode を使います。
文字列、要素、配列、null などをまとめて受けられる、もっとも守備範囲の広い型です。
type LayoutProps = {
children: React.ReactNode;
};
export const Layout = ({ children }: LayoutProps) => {
return <main>{children}</main>;
};
一方、「JSX 要素そのものだけを受け取りたい」というレアケースでは React.ReactElement を使います。
ただし用途はかなり限定的なので、迷ったら ReactNode で覚えておけば大丈夫です。
パターン5: API レスポンスの型
fetch などで取得するデータには、専用の型ファイルを用意しておくと、複数のコンポーネントで使い回せて便利です。src/types/user.ts のような形で切り出しておきます。
// src/types/user.ts
export type User = {
id: number;
name: string;
email: string;
};
呼び出し側では as で当てるのが一番手軽です。
import type { User } from '@/types/user';
const res = await fetch('/api/users/1');
const user = (await res.json()) as User;
ただし、as はあくまで「TypeScript にそう伝えるだけ」で、実際のレスポンスが型と食い違っていても検知できません。もう一歩踏み込みたいときは、実行時にも検証してくれる Zod などのライブラリを使う選択肢もあります。ここでは「そういう道具がある」ことだけ頭の片隅に置いておけば十分です。
型に慣れるための3つの習慣
パターンを覚えたら、あとは日々の書き方で少しずつ慣れていきます。
まず、IDE の型ヒントを積極的に使うこと。VS Code なら変数にマウスを乗せるだけで型が出ます。「今この変数は何型か」を意識するだけで、型への理解が急速に深まります。
次に、any は最終手段にすること。any とは、どんな値でも入れられる型のことで、書いた瞬間からその変数だけ TypeScript の型チェックが実質に切られてしまいます。
型に迷ったらつい any を書きたくなりますが、そこは一度 unknown を検討してみてください。
unknown は「まだ型が分からない」を安全に扱える型で、使う前に型チェックを促してくれます。
最後に、Partial<T> や Pick<T, K> などの Utility Types は必要になってから覚えるので構いません。
最初から全部覚えようとせず、「型を書き換えたい場面が出てきたら調べる」くらいの気持ちで十分です。
まとめ
今回紹介した5パターンを、最後にもう一度整理します。
| パターン | 使う場面 | 主な型 |
|---|---|---|
| props | コンポーネントの引数 | type で定義、?: で Optional |
| useState | 状態管理 | 基本は推論、複雑なら useState<T>() |
| イベント | onChange / onClick / onSubmit | React.ChangeEvent<HTMLInputElement> など |
| children | 子要素を受け取る | 迷ったら React.ReactNode |
| API レスポンス | fetch の結果 | types/ に切り出して as で当てる |
Hooks の使いどころとセットで押さえたい方は、【React入門】よく使うHooks 5選も合わせて読むと、実装の引き出しが一気に広がります。
TypeScriptでは型を書くことでエラーを未然に防ぐことができます。
少しずつ、手を動かしながら仲良くなっていきましょう。
記事を最後まで読んでいただきありがとうございました。また次の記事でお会いしましょう。
