TypeScriptでReactのuseStateをジェネリクスで型指定する方法
生徒
「先生、ReactのuseStateってTypeScriptで型をつけることができるんですか?」
先生
「もちろんできますよ。TypeScriptのジェネリクス(Generics)を使うことで、useStateの状態に型を指定できるんです。」
生徒
「ジェネリクスってちょっと難しそうですけど、どうやって使うんですか?」
先生
「実はとても簡単ですよ。例を見ながら、TypeScriptでReactのuseStateに型を指定する方法を一緒に学んでいきましょう!」
1. ReactのuseStateとは?
Reactは、画面を動的に更新できる人気のあるフロントエンドライブラリです。その中でよく使われるのが、useStateフックです。useStateは、コンポーネント内で「状態(state)」を管理するための機能です。状態とは、たとえば「カウントの数」や「入力フォームの文字」など、変化する値のことです。
まずは、JavaScriptでよく見る基本的なuseStateの使い方を見てみましょう。
import React, { useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>現在のカウント:{count}</p>
<button onClick={() => setCount(count + 1)}>カウントアップ</button>
</div>
);
}
この例では、useState(0)で初期値として0を設定しています。このとき、TypeScriptは「初期値が数値だから、この状態の型はnumberだな」と自動的に判断してくれます。これを型推論(type inference)と呼びます。
2. ジェネリクスを使って型を明示的に指定する
しかし、初期値がnullやundefinedのような場合、TypeScriptは型をうまく推論できないことがあります。たとえば、次のようなコードです。
const [userName, setUserName] = useState(null);
この書き方だと、TypeScriptはuserNameの型をnullとして判断してしまいます。つまり、後から文字列を代入しようとするとエラーになってしまうのです。
そこで登場するのがジェネリクス(Generics)です。useState<型>()のように書くことで、明確に「この変数はどんな型の値を持つか」を指定できます。
const [userName, setUserName] = useState<string | null>(null);
このように書くことで、「初期値はnullだけど、将来的にはstring型の値が入る」ということをTypeScriptに伝えることができます。これが、Reactでジェネリクスを活用する代表的な例です。
3. 実際に動くサンプルを見てみよう
次に、ジェネリクスを使って型を明示した実際のReactコンポーネントを作ってみましょう。
import React, { useState } from "react";
function UserInput() {
const [name, setName] = useState<string | null>(null);
return (
<div>
<input
type="text"
placeholder="名前を入力してください"
onChange={(e) => setName(e.target.value)}
/>
<p>入力された名前:{name}</p>
</div>
);
}
入力欄に文字を打つと、その文字が下に表示されます。
このコードでは、useState<string | null>(null)として型を指定しています。そのため、最初はnullですが、setName関数で文字列を代入できるようになっています。
また、|(パイプ)は「または」という意味のユニオン型(Union Type)です。つまり、「文字列またはnullが入る」という柔軟な型指定ができるわけです。
4. 数値・配列・オブジェクトをuseStateで型指定する
useStateのジェネリクスは、文字列だけでなく、数値・配列・オブジェクトなどにも使えます。
(1)数値を管理する例
const [count, setCount] = useState<number>(0);
この場合、countは常にnumber型の値を持ちます。数値以外の値を入れるとエラーになります。
(2)配列を管理する例
const [items, setItems] = useState<string[]>([]);
string[]は「文字列の配列」という意味です。たとえば、setItems(["リンゴ", "バナナ"])のように使えます。
(3)オブジェクトを管理する例
type User = {
name: string;
age: number;
};
const [user, setUser] = useState<User | null>(null);
このように、オブジェクト型にもジェネリクスを適用できます。初期値がnullの場合は、| nullを追加しておくのがポイントです。
5. useStateジェネリクスのメリット
TypeScriptでuseStateにジェネリクスを使うと、次のようなメリットがあります。
- 型安全:間違った型の値を代入するとコンパイルエラーで気づける。
- 補完が効く:VSCodeなどで入力補助が出るので、コーディングが快適になる。
- バグを防げる:意図しない型のデータが入ることを防止できる。
特に初心者のうちは、何でもany型にしてしまいがちですが、ジェネリクスを使って正確に型を指定することで、より安全で信頼性の高いReactアプリを作れるようになります。
まとめ
TypeScriptとReactのuseStateをジェネリクスで扱う重要性
この記事では、TypeScriptを使ったReact開発において、useStateフックをジェネリクスで型指定する方法について詳しく学んできました。ReactのuseStateは状態管理の基本となる仕組みですが、TypeScriptと組み合わせることで、より安全で分かりやすいコードを書くことができます。特に、状態が数値なのか文字列なのか、配列なのかオブジェクトなのかを明確にすることで、意図しないバグや実行時エラーを未然に防ぐことができます。
初期値が明確な場合はTypeScriptの型推論に任せることもできますが、nullやundefinedを初期値にするケースでは、ジェネリクスによる明示的な型指定が非常に重要になります。useStateにジェネリクスを指定することで、「この状態には将来的にどんな値が入るのか」をコード上ではっきり示すことができ、保守性や可読性が大きく向上します。
ジェネリクスとユニオン型を組み合わせた実践的な考え方
useStateのジェネリクスでは、単一の型だけでなく、ユニオン型と組み合わせた指定がよく使われます。たとえば「文字列またはnull」「オブジェクトまたはnull」といった形です。これは、Reactコンポーネントが最初に表示された時点ではまだ値が存在しないが、ユーザー操作やAPI通信によって後から値が入る、という現実的な状況をそのまま型として表現しています。
このような型指定を行うことで、状態を使う側でも「nullチェックをしなければならない」という意識が自然と生まれ、より丁寧で安全な実装につながります。TypeScriptは、型を通じて開発者に注意点を教えてくれる存在でもあるため、ジェネリクスを正しく使うことはReactとTypeScriptを組み合わせた開発において欠かせないスキルです。
useStateをジェネリクスで型指定したサンプルまとめ
type Todo = {
id: number;
title: string;
completed: boolean;
};
function TodoSample() {
const [todos, setTodos] = useState<Todo[]>([]);
const [selectedTodo, setSelectedTodo] = useState<Todo | null>(null);
return (
<div>
<button
onClick={() =>
setTodos([
{ id: 1, title: "学習する", completed: false },
{ id: 2, title: "復習する", completed: true },
])
}
>
Todoを設定
</button>
<ul>
{todos.map((todo) => (
<li key={todo.id} onClick={() => setSelectedTodo(todo)}>
{todo.title}
</li>
))}
</ul>
{selectedTodo && (
<p>選択中のTodo:{selectedTodo.title}</p>
)}
</div>
);
}
この例では、配列・オブジェクト・nullを含む状態をすべてジェネリクスで明示的に型指定しています。これにより、setTodosやsetSelectedTodoに誤った型の値を渡すと、コンパイル時点でエラーとして検出されます。ReactのuseStateとTypeScriptのジェネリクスを正しく組み合わせることで、実務レベルでも安心して使える状態管理が実現できます。
生徒
「useStateにジェネリクスを指定する意味が、やっと分かってきました。型をちゃんと決めておくと、後で安心して使えるんですね。」
先生
「その通りです。Reactの状態はアプリの中心になるので、型をあいまいにするとバグの原因になりやすいんです。」
生徒
「nullを初期値にする場合は、ユニオン型で指定するのが大事だというのも理解できました。」
先生
「とても良いポイントですね。TypeScriptは、そうした曖昧な状態を型で表現するのが得意なんですよ。」
生徒
「これからは、useStateを書くときに『この状態にはどんな型が入るんだろう』って考えるようにします。」
先生
「その意識があれば、ReactとTypeScriptを使った開発で一段レベルアップできます。ぜひ実際のアプリでも活用してみてください。」