TypeScriptでイベントハンドラを型定義する方法を完全解説!初心者でもわかるReactの基本
生徒
「Reactでボタンをクリックしたり入力フォームを使ったりするときにイベントハンドラを書きますよね?TypeScriptだと型も書かないといけないんですか?」
先生
「そうですね。TypeScriptを使うとイベントハンドラにも型を付けることができます。型を付けると間違った書き方を防げるので安心です。」
生徒
「イベントハンドラの型ってどうやって書くんですか?難しそうです…」
先生
「大丈夫ですよ。いくつかの基本的な型を覚えておけば簡単に使えるので、一緒に見ていきましょう!」
1. イベントハンドラとは?
イベントハンドラとは、ボタンをクリックしたり、入力フォームに文字を入力したときに呼び出される関数のことです。例えば、「ボタンを押したらメッセージを変える」という動きはイベントハンドラで実現します。
JavaScriptだけでも書けますが、TypeScriptを使うと「どんなイベントを受け取る関数なのか」を型で指定できるため、安心してプログラムを書けます。
2. クリックイベントの型定義
最もよく使うのはボタンのクリックイベントです。ReactとTypeScriptではReact.MouseEventHandler<HTMLButtonElement>を使います。
import React, { useState } from "react";
const ClickButton: React.FC = () => {
const [message, setMessage] = useState("こんにちは!");
const handleClick: React.MouseEventHandler<HTMLButtonElement> = (event) => {
setMessage("ボタンがクリックされました!");
};
return (
<div>
<h1>{message}</h1>
<button onClick={handleClick}>クリックしてみよう</button>
</div>
);
};
export default ClickButton;
ここで使っているReact.MouseEventHandler<HTMLButtonElement>は「マウスイベントを扱うボタン専用の関数です」という意味です。
3. 入力フォームのイベント型
次に、入力フォーム(input要素)の値を受け取るイベントハンドラです。こちらはReact.ChangeEventHandler<HTMLInputElement>を使います。
const InputForm: React.FC = () => {
const [text, setText] = useState("");
const handleChange: React.ChangeEventHandler<HTMLInputElement> = (event) => {
setText(event.target.value);
};
return (
<div>
<input type="text" value={text} onChange={handleChange} />
<p>入力内容: {text}</p>
</div>
);
};
event.target.valueは「入力された文字」を意味します。型を指定しているので、数値を入れようとしてもエラーになります。
4. フォーム送信イベントの型
フォーム全体を送信する場合は、React.FormEventHandler<HTMLFormElement>を使います。
const SubmitForm: React.FC = () => {
const [text, setText] = useState("");
const handleSubmit: React.FormEventHandler<HTMLFormElement> = (event) => {
event.preventDefault(); // ページのリロードを防ぐ
alert("送信された内容: " + text);
};
return (
<form onSubmit={handleSubmit}>
<input type="text" value={text} onChange={(e) => setText(e.target.value)} />
<button type="submit">送信</button>
</form>
);
};
event.preventDefault()は、ブラウザの標準動作(リロード)を止めるために使います。
5. よく使うイベントハンドラ型一覧
初心者の方がよく使うイベントの型をまとめると次のようになります。
- クリックイベント →
React.MouseEventHandler<HTMLButtonElement> - 入力フォームの変更 →
React.ChangeEventHandler<HTMLInputElement> - フォーム送信 →
React.FormEventHandler<HTMLFormElement> - チェックボックスの変更 →
React.ChangeEventHandler<HTMLInputElement> - セレクトボックスの選択 →
React.ChangeEventHandler<HTMLSelectElement>
このように、使う要素に応じて型を指定するだけで、エディタが入力補完してくれるのでミスが減ります。
6. 型定義を使うメリット
TypeScriptでイベントハンドラを型定義すると次のメリットがあります。
- 入力補完が効いて使えるプロパティがすぐに分かる
- 間違った要素に対してイベントを使おうとするとエラーで気づける
- 複雑なフォームでも安心して管理できる
最初は少し長く感じるかもしれませんが、慣れるととても便利で安全な方法になります。
まとめ
今回の記事では、ReactとTypeScriptを組み合わせて開発を行う際に避けては通れない、イベントハンドラの型定義について詳しく解説してきました。フロントエンド開発において、ユーザーの操作(クリックや入力、送信など)を正確に制御することは、アプリケーションの品質を左右する非常に重要な要素です。
TypeScriptでイベントを扱う重要性
JavaScriptでの開発に慣れていると、最初は「なぜわざわざ型を指定しなければならないのか」と手間に感じることもあるかもしれません。しかし、TypeScriptを導入する最大の恩恵は、「実行前にエラーに気づけること」と「開発効率が圧倒的に向上すること」にあります。
例えば、event.target.valueを参照しようとした際、そのイベントが本当に値を保持している要素(inputやtextareaなど)から発生したものかどうかを、コードを動かす前にエディタが教えてくれます。これにより、本番環境で「undefinedで動かない」といった初歩的なバグを劇的に減らすことができるのです。
実践的な応用例:複数のイベントを組み合わせる
これまでの学習を踏まえて、もう少し実践的な「ログインフォーム」のようなコンポーネントを考えてみましょう。ここでは、複数の入力項目の型定義と、送信時の処理を一つのコンポーネントにまとめています。
import React, { useState } from "react";
/**
* ユーザー情報の型定義
*/
type UserFormState = {
email: string;
role: "admin" | "user";
};
const AdvancedForm: React.FC = () => {
const [formData, setFormData] = useState<UserFormState>({
email: "",
role: "user",
});
// テキスト入力とセレクトボックスの変更をハンドル
const handleInputChange: React.ChangeEventHandler<HTMLInputElement | HTMLSelectElement> = (event) => {
const { name, value } = event.target;
setFormData((prev) => ({
...prev,
[name]: value,
}));
};
// フォーム送信のハンドル
const handleSubmit: React.FormEventHandler<HTMLFormElement> = (event) => {
event.preventDefault();
console.log("送信データ:", formData);
alert(`送信完了: ${formData.email} (${formData.role})`);
};
return (
<div className="container mt-4">
<form onSubmit={handleSubmit} className="border p-4 shadow-sm">
<div className="mb-3">
<label className="form-label">メールアドレス</label>
<input
type="email"
name="email"
className="form-control"
value={formData.email}
onChange={handleInputChange}
required
/>
</div>
<div className="mb-3">
<label className="form-label">権限</label>
<select
name="role"
className="form-select"
value={formData.role}
onChange={handleInputChange}
>
<option value="user">一般ユーザー</option>
<option value="admin">管理者</option>
</select>
</div>
<button type="submit" className="btn btn-primary">
登録する
</button>
</form>
</div>
);
};
export default AdvancedForm;
型定義をマスターするための3つのポイント
- ジェネリクス(< >)を意識する:
React.ChangeEventHandler<HTMLInputElement>のように、山括弧の中に対象となるHTML要素を記述するのがReactの基本ルールです。 - React.FC(Function Component)を活用する: コンポーネント自体に型を付けることで、propsの型定義もスムーズに行えるようになります。
- エディタの補完を信じる: 自分で全ての型を暗記する必要はありません。
React.まで入力して、出てくる候補から適切なイベント(Click, Change, Formなど)を探す癖をつけましょう。
TypeScriptの導入は、最初は壁を感じるかもしれませんが、一度慣れてしまえば「これなしでは開発できない」と感じるほど強力な味方になります。今回紹介したパターンは、実際の現場で最も頻繁に使われるものばかりですので、ぜひ自分のコードに組み込んで、エラーのない快適なコーディング体験を手に入れてください。
生徒
「先生、ありがとうございました!イベントハンドラの型定義、最初は難しそうだと思っていましたが、基本の型が決まっていることが分かって少し安心しました。」
先生
「それは良かったです。最初は ChangeEventHandler や MouseEventHandler などの名前を覚えるのが大変かもしれませんが、使っているうちに出番が多いものは自然と身についていきますよ。」
生徒
「さっきのログインフォームのコードで、HTMLInputElement | HTMLSelectElement のように縦棒(|)を使っているところがありましたね。あれはどういう意味ですか?」
先生
「鋭いですね!それは『ユニオン型』と言って、『input要素か、もしくはselect要素のどちらか』を指しています。一つの関数で複数の要素を扱いたい時には、こうして型を広げてあげることができるんです。」
生徒
「なるほど!柔軟に指定できるんですね。今までなんとなく any 型を使って逃げていた部分もあったのですが、これからはしっかり型を指定して、エディタに助けてもらいながら書いてみます。」
先生
「素晴らしい意気込みですね。any を卒業することが、TypeScriptマスターへの第一歩です。型がしっかりついていると、数ヶ月後に自分でコードを見返したときも『ここで何をしようとしていたか』がすぐに分かるので、未来の自分へのプレゼントにもなりますよ。頑張りましょう!」
生徒
「はい、頑張ります!他にもスクロールイベントやキーボードイベントの型も気になってきたので、少しずつ調べていこうと思います!」