TypeScript の Event.target 型について
TypeScript でイベントリスナーを扱うとき、Event.target
が Element
型ではないことに疑問を持つかもしれません。その理由は、すべてのイベントターゲットが要素 (Element) であるとは限らないからです。
EventTarget インターフェースは、イベントが発生する可能性のあるオブジェクトの基盤となるものです。Element
(要素)、document
(ドキュメントオブジェクト)、window
(ウィンドウオブジェクト) などがイベントターゲットの例ですが、他にも XMLHttpRequest
や AudioNode
などもイベントを発生させることができます。
TypeScript は型安全性のために、Event.target
をより汎用的な EventTarget
型として定義しています。これにより、イベントが発生したオブジェクトの種類を正確に把握できない場合でも、イベントリスナー関数を定義することができます。
Event.target
を Element
型として使いたい場合
もし、イベントリスナー関数が確実に Element
を対象としていることが分かっているなら、次のような方法で型を絞ることができます。
- アサーション (Type Assertion)
function handleClick(event: Event) {
const targetElement = event.target as HTMLElement; // アサーションを使って `Element` 型にキャスト
console.log(targetElement.id); // id プロパティにアクセス可能
}
- 型チェック
function handleClick(event: Event) {
if (event.target instanceof HTMLElement) {
const targetElement = event.target;
console.log(targetElement.id);
} else {
// 対象が Element でない場合の処理
}
}
- 確実に
Element
を対象としている場合は、アサーションや型チェックで型を絞ることができる。 Event.target
はすべてのイベントターゲットを網羅するため、EventTarget
型になっている。
イベントリスナーで Event.target
を使用すると、Element
型ではないことに気づくかもしれません。これは、すべてのイベントが要素 (Element) を発生源とするわけではないからです。
Event.target
の型がEventTarget
であること
次のコードは、ボタンクリックイベントに対するリスナー関数です。Event.target
の型は EventTarget
であり、要素のプロパティ (例えば、id
) には直接アクセスできません。
const button = document.getElementById('myButton');
button.addEventListener('click', (event: Event) => {
console.log(event.target); // EventTarget 型
});
イベントリスナー関数が確実に Element
を対象としていることが分かっている場合は、アサーション を使って型を絞ることができます。アサーションは、コンパイラーに "本当は Element
型のはず" と伝える記法です。
const button = document.getElementById('myButton');
button.addEventListener('click', (event: Event) => {
const targetElement = event.target as HTMLElement; // アサーションを使って Element 型にキャスト
console.log(targetElement.id); // id プロパティにアクセス可能
});
- 型チェックによる
Element
型の絞り込み
型チェック を使って、event.target
が Element
型かどうかを確認してからアクセスすることもできます。
const button = document.getElementById('myButton');
button.addEventListener('click', (event: Event) => {
if (event.target instanceof HTMLElement) {
const targetElement = event.target;
console.log(targetElement.id);
} else {
// 対象が Element でない場合の処理
}
});
- イベントリスナーの対象が確実に
Element
であることが分かっている場合は、アサーションや型チェックで型を絞ることができます。
TypeScript の Event.target
型と、型を絞る方法 (代替案)
イベントリスナーで Event.target
の型を Element
に絞る方法として、アサーションと型チェックを紹介しましたが、他にも選択肢があります。
カスタムイベントを使用した型指定
DOM イベント以外にも、自分で作成したイベントオブジェクト (カスタムイベント) を利用することができます。カスタムイベントでは、イベントオブジェクトのプロパティの型を自由に定義できます。
interface ButtonClickEvent extends CustomEvent {
detail: {
buttonElement: HTMLButtonElement;
};
}
const button = document.getElementById('myButton');
button.addEventListener('buttonClick', (event: ButtonClickEvent) => {
const clickedButton = event.detail.buttonElement;
console.log(clickedButton.id); // 型が保証されているので、安心して id プロパティにアクセスできる
});
// イベント発行時
button.dispatchEvent(new CustomEvent('buttonClick', {
detail: {
buttonElement: button,
},
}));
ターゲット要素を直接取得
イベントリスナーではなく、イベント発生時にターゲット要素を DOM 操作で直接取得する方法もあります。
const button = document.getElementById('myButton');
button.addEventListener('click', () => {
const clickedButton = button as HTMLButtonElement; // アサーションに近いですが、こちらはイベントオブジェクトではなくDOM要素に対して行います
console.log(clickedButton.id);
});
選択のポイント
- ターゲット要素を直接取得する方法の方がシンプルですが、型安全性が少し落ちます。確実に
Element
であることが分かっている場合に限って使用しましょう。 - カスタムイベントは、イベントの種類を明確に定義でき、型安全性が向上します。ただし、イベントリスナー側でイベントオブジェクトを生成・発行する処理が必要になります。
typescript event-listener