TypeScriptでstyled-componentsの"as" propにNextLinkコンポーネントを渡したい時


実装したかったこと

<label>要素において、リンク付きにしたい箇所とリンクなしにしたい箇所があったため、以下のように2通りのケースに対応できるようにしたい。

  • <label>をベースにスタイルを定義する。
  • <label><a>に置き換えて、スタイルは全く同じものを使用する。

やったこと

"as" prop を使用し解決しようとしました。

export const CategoryLink: React.FunctionComponent<LinkProps> = ({
    title = 'メンヘラ',
    href,
}) => (
    <CategoryLabel as={Link} href={href}>
        {title}
    </CategoryLabel>
);

これでいけると思いました。怒られました

Types of property 'as' are incompatible.
    Type 'typeof Link' is not assignable to type 'string | UrlObject | undefined'.
          Type 'typeof Link' has no properties in common with type 'UrlObject'.

これは、

  • "as" の型が間違ってるよ。
  • Link の型は ("as" で要求されている) string | UrlObject | undefined のどれにも該当しないよ。
  • Link の型は (唯一オブジェクトとして検査する価値のある) UrlObject のプロパティと構成が全く違うよ。

と言われました。とりあえずエラーをもとに、styled-componet ts as linkのワードでググりました。すると同じようなことで悩んでるZeit公式のissueにぶつかりました。

こうしたらいけると公式が言うからいけるのでしょう!やってみます。

export const CategoryLink: React.FunctionComponent<LinkProps> = ({
    title = 'メンヘラ',
    href,
}) => (
    <Link href={href} passHref>
        <CategoryLabel as="a">
            {title}
        </CategoryLabel>
    </Link>
);

できました!!!!!!!!

(レイアウトは今作り段階なのでゴミです許してください)

何がダメだったのか?

こちらはNext.jsの公式 Routingのところから抜粋したものです。

  1. Next.jsのRoutingで設定してるasとstyled-componentで設定しようとしたasが被っていた。
  2. Next.jsのRoutingのasをTypeScript側は参照しようとしているのに、私がstyled-componentのasを提示した。

このため

Link の型は (唯一オブジェクトとして検査する価値のある) UrlObject のプロパティと構成が全く違うよ。

と言うエラーになったと言うわけです。

ちなみに

現時点でわかったasが受け取れるものは以下です。

  • HTMLのネイティブ要素名(文字列)
  • styled-componentで作ったコンポーネント(オブジェクト)

なので下のコードも通ります。

export default CategoryLabel;

const Foo = styled.h1`
`;

export const CategoryLink: React.FunctionComponent<LinkProps> = ({
    title = 'メンヘラ',
    href,
}) => (
    <CategoryLabel as={Foo}>
        {title}
    </CategoryLabel>
);

以上です!
メンヘラなので、何か助言あれば優しい言葉でお願いします。