🎫
Event Types in Svelte and TypeScript
The Problem
SvelteでTypeScriptを使っていると、しばしばこの種のエラーに遭遇することがあるでしょう。
<script lang="ts">
// @errors: 7006
const onChange = (e) => {};
</script>
<input on:change={onChange} />
onChange
関数の引数であるe
にどのような型を与えるべきかは必ずしも明確ではありません。
DOMごとにイベントには異なる型が与えられているためです。
幸い、いくつか解決策があります。
Solution 1: 仮の関数を渡し、ホバーして型をコピーする
最初の解決策は、on:change
に仮の関数を渡して、その関数の引数の型をホバーしてコピーする方法です。
何やら複雑な型が表示されていますが、これをコピーしてonChange
関数の型として使うことができます。
<script lang="ts">
const onChange = (e: Event & {
currentTarget: EventTarget & HTMLInputElement;
}) => {};
</script>
<input on:change={onChange} />
SvelteHTMLElements
から型を取得する
Solution 2:
こちらにHTML DOMの型に関する記述があります。
これによれば、SvelteHTMLElements
からDOMの属性の種類とそれぞれの属性が取るべき関数の型を取得することができます。
<script lang="ts">
import type { SvelteHTMLElements } from 'svelte/elements';
type InputOnChangeEvent = SvelteHTMLElements['input']['on:change']
const onChange: NonNullable<InputOnChangeEvent> = (e) => {}
</script>
<input on:change={onChange} />
これにより、onChange
の引数e
の型を決定できます。
Solution 3: Type Helperを定義して引数の型を取得する
Solution 2のやり方はうまく行くのですが、毎回SvelteHTMLElements
から型情報を取得するのは面倒です。
そのため、Type Helperを定義すると便利です。
import type { SvelteHTMLElements, EventHandler } from 'svelte/elements';
type Nullish<T> = T | null | undefined;
type GetEventHandlers<T extends keyof SvelteHTMLElements> = Extract<
keyof SvelteHTMLElements[T],
`on:${string}`
>;
export type SvelteHTMLElementEvent<
TElement extends keyof SvelteHTMLElements,
THandler extends GetEventHandlers<TElement>
> = SvelteHTMLElements[TElement][THandler] extends Nullish<EventHandler<infer TEvent, infer _>>
? TEvent
: never;
このType Helperを用いると、onChange
の引数e
に与えるべき型を簡単に取得できます。
<script lang="ts">
import type { SvelteHTMLElementEvent } from 'svelte-html-event';
const onChange = (e: SvelteHTMLElementEvent<'input', 'on:change'>) => {}
</script>
<input on:change={onChange} />
このコードはライブラリ化してnpmに公開しているので、
bun install svelte-html-event
でインストールできます。
まとめ
すでに自分の関わっているプロジェクトではSolution 3を使っています。
皆さんも是非お試しください〜
Discussion