2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

svelte:elementのラッパーコンポーネントを作る

Posted at

概要

今後の変更への対応を楽にするため、基本的にはsvelte:xxx系のコンポーネントはラップしたものを使うようにする方針です。

svelte:elementも同様に行いたいのと、Markuplintのas恩恵を受けたいのでsvelte:elementのラッパーを作ります。

svelte:elementのラッパー

インターフェースとしてはasでHTML要素名(aとかbuttonとか)を受け取り、
asで指定したHTML要素が受け取ることのできるpropsだけを追加で受け取れるようにしたいです。

HTML要素名

keyof HTMLElementTagNameMapで取得できます。

これの存在を知りませんでした。そんなんあったんかい。

いけそうに思えるNG例

<script lang="ts" generics="P extends SvelteHTMLElements[T], T extends keyof HTMLElementTagNameMap">
	import type { SvelteHTMLElements } from 'svelte/elements';

	const {
		as,
		children,
		...elementProps
	}: {
		as: T;
	} & P = $props();
</script>

<svelte:element this={as} {...elementProps}>
	{@render children?.()}
</svelte:element>

いけそうな雰囲気を感じますが、
これだと{...elementProps}の部分でUnion型複雑すぎて無理ィ!というエラーが出ます。

Expression produces a union type that is too complex to represent.ts(2590)

正解

<script lang="ts" generics="P extends SvelteHTMLElements[T], T extends keyof HTMLElementTagNameMap">
	import type { SvelteHTMLElements } from 'svelte/elements';

	type OmitOldOnProps<T> = {
		[P in keyof T as P extends `on:${string}` ? never : P]: T[P];
	};

	const {
		as,
		children,
		...elementProps
	}: {
		as: T;
	} & OmitOldOnProps<P> = $props();
</script>

<svelte:element this={as} {...elementProps}>
	{@render children?.()}
</svelte:element>

on:を取り除くとうまくいきます。on:周りのSvelte4の型が残っているとその分型周りがややこしくなるのでしょうか…

これにて一件落着!

Tips: SvelteでGenericsを使うとき使っていた謎のテクニック(今は気にしなくてOK)

<script lang="ts" generics="P extends SvelteHTMLElements[T], T extends keyof HTMLElementTagNameMap">
	import type { SvelteHTMLElements } from 'svelte/elements';
</script>

古いバージョンのSvelteだとこれがSvelteHTMLElementsなんぞ知らん!というlintエラーになっていました。
SvelteHTMLElementsがgenericsよりも後に書かれているのが原因みたいなので、script moduleを悪用(?)すると回避できます。

<script lang="ts" module>
	import type { SvelteHTMLElements } from 'svelte/elements';
</script>
<script lang="ts" generics="P extends SvelteHTMLElements[T], T extends keyof HTMLElementTagNameMap">
</script>

古いバージョンのSvelteを使っている人は覚えておいて損はないテクニックです。

参考

こちらを試してみたところ解決しました。ありがとう…🙏

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?