Svelteの$$Genericをつかってみよう
注意
2023/07/19
この記事は古くなりました。
どうやらこの構文は不採用になり、
代わりにこちらの構文が採用になるようです。
やりたかったこと
- プラスアイコンだけが見えているボタンコンポーネントを作りたい
- aria-labelで何をするボタンなのかを読み上げさせたい
- aria-labelに空の文字を入れたくない(意味がないので)
- aria-labelが空になってしまう場合そもそもボタン自体表示しないようにしてミスに気付けるようにしたい
この3点です。
そもそも
requiredProp: string
なpropsって空文字を受け付けたくないですよね。
requiredとは…?
基本的に空文字を入れることはないのでいままで気にしたことはなかったのですが、よく考えるとなにもrequiredにできていないことに気づいたので空文字を許可しない型探しに出ました。
StackOverflowに同じような質問が投げられており、どうやら関数だといい感じに書けるということがわかりました。
Reactは素のJSに近い形で書けるので関数コンポーネントでこれを書くのは割と簡単で、上記記事にも答えが書いてあります。
じゃあSvelteでやるとどうなるのかというと以下です。
Point!!部分がポイントです。
js部分
//<script lang="ts"> // svelteのハイライトが効かないのでコメントアウト
import Ico from '$lib/components/svg/Ico.svelte' // アイコンを表示
type T = $$Generic<string> // Point!!①
export let notEmptyLabel: T extends '' ? never : T // Point!!②
$: label = notEmptyLabel.trim()
//</script>
Point①
まず、SvelteでGenericsを使う場合は$$Generic
を使います。
これなのですが、探した感じドキュメントには載っていなかったような…
ホバーしても何にも表示されないのでなんもわからん…になりがちな気がします。
僕は<string>
できることに気づかなかったので なんとなくできないものだと思い込んでいたのですが、YENDさんからのリプライでふと気づきを得て試しに書いてみたところどうやら型変数を渡せるようです!(ありがとうございました!)
Point②
ここまでくればあとは普通のTypeScriptです。
T extends '' ? never : T
の部分で型を分岐してあげれば空文字とサヨナラできます。
これで必須プロパティが空で入ってくるのは型で防げましたが、半角スペースを入れたりしてくる輩がいるかもしれないので一応
notEmptyLabel.trim()
しています。
もっとちゃんとやりたい人は頑張ってください。
html部分
{#if !!label}
<button
type="button"
aria-label={label}
on:click
class="grid place-items-center w-12 h-12 bg-white"
>
<span class="block w-6">
<Ico name="add" />
</span>
</button>
{/if}
{#if !!label}
で分岐させています。
まとめ
こんな感じで、Svelteでは$$Generic
を使うとジェネリクスを使用することができます。
応用するとセレクトボックスコンポーネントに配列を渡して値にセットできるのはその中のものだけ とかもできますね。
Svelte、結構いいと思うんですが記事が少ないのでとっつきにくさがあるかもしれません。
簡単なものからよくある実装の例など、いろんな記事が読みたいですね!
Discussion