any型で諦めない React.EventCallback

React x TypeScript の鬼門のひとつに「props に記述する EventCallback の適切な書き方が分からない」というものがあります。さて、このコンポーネントの `type Props` どう型定義するべきでしょうか？

```tsx
const View: React.FC<Props> = props => (
  <form onSubmit={props.onSubmit}>
    <input
      type="text"
      onClick={props.onClick}
      onChange={props.onChange}
      onKeyPress={props.onkeypress}
      onBlur={props.onBlur}
      onFocus={props.onFocus}
    />
    <div onClick={props.onClickDiv} />
  </form>
)
```

「とくに困らないので〜」という理由でつぎの様に妥協していませんか？ 部分的妥協は、TypeScript の良い点でもありますが、せっかくなのできちんとしたいですよね。

```tsx
type Props = {
  onClick: (event: any) => void
  onChange: (event: any) => void
  onkeypress: (event: any) => void
  onBlur: (event: any) => void
  onFocus: (event: any) => void
  onSubmit: (event: any) => void
  onClickDiv: (event: any) => void
}
```

ただ、EventCallback の型はたくさんあるし、初見ではどれを指定すれば良いのか…迷いますよね。自分は普段、VSCode を使っているため、迷ったときは VSCode のコードヒントに頼ることにしています。つぎのキャプチャは、onClick まで空で書いて、 onClick にマウスオーバーした時のものです。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/59071/32ed4019-704a-144c-ddc8-2d9681f54fe4.png)

答えが全部、型推論に書いてありましたね！これで指定方法が分かったので、コピペします。

```tsx
type Props = {
  onClick: (event: React.MouseEvent<HTMLInputElement>) => void
  onChange: (event: React.ChangeEvent<HTMLInputElement>) => void
  onkeypress: (event: React.KeyboardEvent<HTMLInputElement>) => void
  onBlur: (event: React.FocusEvent<HTMLInputElement>) => void
  onFocus: (event: React.FocusEvent<HTMLInputElement>) => void
  onSubmit: (event: React.FormEvent<HTMLFormElement>) => void
  onClickDiv: (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => void
}

```

# Generics に指定している型はなに？

さて、`React.MouseEvent<XXX>`や`React.ChangeEvent<XXX>`の様に、Generics に`HTMLInputElement`型や `HTMLDivElement`型など、いろいろな型が指定されていますね…。

import もしていないし、この型はどこから来たもので、いったいこれらの指定で何が変わるのでしょうか？ `HTMLInputElement`型の中身をみてみましょう。型にマウスを当て、Option キーを押しながらマウスクリックすることで、いつもの様に型定義へジャンプします。

```@types/react/global.d.ts
interface HTMLDivElement extends HTMLElement { }
interface HTMLDListElement extends HTMLElement { }
interface HTMLEmbedElement extends HTMLElement { }
interface HTMLFieldSetElement extends HTMLElement { }
interface HTMLFormElement extends HTMLElement { }
interface HTMLHeadingElement extends HTMLElement { }
interface HTMLHeadElement extends HTMLElement { }
interface HTMLHRElement extends HTMLElement { }
interface HTMLHtmlElement extends HTMLElement { }
interface HTMLIFrameElement extends HTMLElement { }
interface HTMLImageElement extends HTMLElement { }
interface HTMLInputElement extends HTMLElement { }
interface HTMLModElement extends HTMLElement { }
interface HTMLLabelElement extends HTMLElement { }
interface HTMLLegendElement extends HTMLElement { }
interface HTMLLIElement extends HTMLElement { }
```

紙面の都合上省略していますが、こんなにあるのか…とたくさん出てきましたね！よくみてみると…。ほとんどが `extends HTMLElement { }` ですね。名前が違うだけで、全部一緒じゃないですかw でも実は、interface にはオーバーロードの仕組みがあり、それぞれの型は、`@types/react/global.d.ts` で明示されていない定義を上書きしています。元の `HTMLInputElement`型は一体どこからきているかというと…

![image.png](https://qiita-image-store.s3.amazonaws.com/0/59071/cf260462-f2d5-2fe3-fd6b-a0264e4544c7.png)

キャプチャ右側のとおり一覧で表示されます。 `lib.dom.d.ts` と `global.d.ts` で定義されていることが分かりますね。`global.d.ts` は、@types/react で提供されている定義、`lib.dom.d.ts` は TypeScript から提供されている定義です。

「`lib.dom` なんて入れた覚えが無いよー」と不思議に思うかもしれませんが、これの出どころは、tsconfig の lib 指定です。tsconfig の lib 指定が無い場合、他の指定内容(targetなど)に応じて適切なデフォルト lib が import されるためこの様になります。出所不明だった `HTMLInputElement`型 はこの様に、interface のオーバーロードの仕組みで塗り重ねられていたんですね。


# 必要に応じて、抽象度を見極める

`onClick` を Divタグ・Buttonタグなど、複数に適用したい場合、詳細な型である `HTMLInputElement`型や `HTMLDivElement`型 を指定してしまうと、汎用的でなく不便な場合があります。そういう時は、継承元の `HTMLElement`型など、抽象的な型を指定すれば良いです。

```tsx
type Props = {
  onClick: (event: React.MouseEvent<HTMLElement, MouseEvent>) => void
}
```

EventCallback の引数型をしっかり享受したい場合は、型推論通りの内容を使いましょう。

```tsx
type Props = {
  onClickButton: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void
}

```