TypeScript

【TypeScript】event.targetに型をつけたい

事象

Next.js × TypeScriptを使用したアプリで、クリックしたボタンのIDと紐づいたモーダル要素を表示したかったのですが、IDを参照できずエラーとなってしまいました。

以下のコードはReact × TypeScript製で、クリックしたボタンのIDをコンソールに出力する失敗例です。

event.target.idでボタンのIDを取得しようとしましたが、うまくいきません。

import React, { MouseEvent, ReactNode } from 'react';
import './App.css';

const Button = ({
  children,
  id,
  handleClick,
}: {
  children: ReactNode;
  id: string;
  handleClick?: (event: MouseEvent<HTMLButtonElement>) => void;
}) => {
  return (
    <button onClick={handleClick} id={id}>
      {children}
    </button>
  );
};

function App() {
  const handleClick = (event: MouseEvent<HTMLButtonElement>) => {
    console.log(event.target.id);
  };
  return (
    <div className="App">
      <Button handleClick={handleClick} id="button">
        ボタン
      </Button>
    </div>
  );
}

export default App;

原因

event.targetの型が確定していないため、このようなエラーが発生します。

event.targetは実際にイベントが発生した要素の情報を取得するため、TypeScriptコンパイラーから見ると何の要素なのかがわからないのです。

App関数を以下のように編集すると、わかりやすいです。

ボタンクリックで取得した要素は、button要素ではなくstrong要素になってしまいます。

function App() {
  const handleClick = (e: MouseEvent<HTMLButtonElement>) => {
    console.log(event.target);
    console.log(event.currentTarget);
  };
  return (
    <div className="App">
      <Button handleClick={handleClick} id="button">
        <strong>ボタン</strong>
      </Button>
    </div>
  );
}

解決方法

解決方法は2つあります。

event.currentTargetを使う

event.currentTargetはイベントハンドラーを登録した要素の情報を取得します。

要素や型が特定されるため、ボタンのIDを取得することができます。

const handleClick = (event: MouseEvent<HTMLButtonElement>) => {
  console.log(event.currentTarget.id);
};

instanceofで型チェックを行う

targetプロパティを使う方法もあります。

TypeScriptコンパイラーがevent.targetの型を特定できるようにinstanceof演算子で解決してあげます。

event.targetの対象がボタン要素じゃない場合は処理が終了するので、エラーを回避できます。

const handleClick = (event: MouseEvent<HTMLButtonElement>) => {
  if (!(event.target instanceof HTMLButtonElement)) {
    return;
  }
  console.log(event.target.id);
};

参考

TypeScriptでEventの取り扱いがめんどくさ過ぎる。。。
https://zenn.dev/koduki/articles/0f8fcbc9a7485b

イベントオブジェクトの target と currentTarget の違いとそれに伴う TypeScript の挙動を調べてみた
https://zenn.dev/yuyao17/articles/e0a54bd14ead9b

Event.currentTargetとEvent.targetの違いについて
https://www.javadrive.jp/javascript/event/index9.html