🎫

Event Types in Svelte and TypeScript

2023/11/05に公開

The Problem

SvelteでTypeScriptを使っていると、しばしばこの種のエラーに遭遇することがあるでしょう。

<script lang="ts">
    // @errors: 7006
    const onChange = (e) => {};
</script>

<input on:change={onChange} />

onChange関数の引数であるeにどのような型を与えるべきかは必ずしも明確ではありません。
DOMごとにイベントには異なる型が与えられているためです。

幸い、いくつか解決策があります。

Solution 1: 仮の関数を渡し、ホバーして型をコピーする

最初の解決策は、on:changeに仮の関数を渡して、その関数の引数の型をホバーしてコピーする方法です。

on:change hover

何やら複雑な型が表示されていますが、これをコピーしてonChange関数の型として使うことができます。

<script lang="ts">
  const onChange = (e: Event & {
    currentTarget: EventTarget & HTMLInputElement;
  }) => {};
</script>

<input on:change={onChange} />

Solution 2: SvelteHTMLElementsから型を取得する

https://svelte.jp/docs/typescript#enhancing-built-in-dom-types

https://github.com/sveltejs/svelte/blob/5a8c1d2cafe8217aa1a6408b024571d3d655a431/packages/svelte/elements.d.ts

こちらに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

でインストールできます。

https://github.com/ryoppippi/svelte-html-event

まとめ

すでに自分の関わっているプロジェクトではSolution 3を使っています。
皆さんも是非お試しください〜

Discussion