Astro の Polymorphic を使う時に any にしないためのデフォルト値
Astro には、複数の HTML 要素を受け入れ可能なコンポーネントを作る時に便利な TypeScript の型の Polymorphic があります。例えば、<a>
と <button>
のいずれかが HTML 要素のボタンコンポーネントを作る時などに役立ちます。
この Polymorphic の例として、ドキュメントにはこのように書かれています。
---
import type { HTMLTag, Polymorphic } from 'astro/types';
type Props<Tag extends HTMLTag> = Polymorphic<{ as: Tag }>;
const { as: Tag, ...props } = Astro.props;
---
<Tag {...props} />
ただ、この例のままでは Astro.props
が any
になります。
any
にしないためには、Astro.props is type as any when use Polymorphic にあるようにデフォルト値が必要です。button
をデフォルト値に設定してみます。
---
import type { HTMLTag, Polymorphic } from 'astro/types';
type Props<Tag extends HTMLTag = 'button'> = Polymorphic<{
as: Tag;
}>;
const { as: Tag = 'button', ...props } = Astro.props;
---
<Tag {...props} />
これで Astro.props
の型はこのようになります。
(property) props: Props<"button">
余談ですが、HTMLTag
は全ての HTML 要素の型です。私は安全な HTMLTag
は使いつつ、必要な HTML 要素を Extract
で抽出するのを好んでいます。ボタンコンポーネントであれば <a>
と <button>
を抽出します。また、<a>
の場合は href
属性を必須にするように作っています。
---
import type { HTMLTag, Polymorphic } from 'astro/types';
type Props<Tag extends Extract<HTMLTag, 'a' | 'button'> = 'button'> =
Polymorphic<{
as: Tag;
}> &
(Tag extends 'a' ? { href: string | URL } : {});
const { as: Tag = 'button', ...props } = Astro.props;
---
<Tag class:list={'btn'} {...props}>
<slot />
</Tag>