Home > Software design >  Using 'as' polymorphic prop of styled-components with typescript
Using 'as' polymorphic prop of styled-components with typescript

Time:11-15

I was trying to implement a Typography react component.
As you can see below, I got variant as an input prop and used it as index of VariantsMap object to get corresponding html tag name.

Then I used styled-components 'as' polymorphic prop to render it as selected html tag.

but I keep get this error :
No overload matches this call. Overload 1 of 2, '(props: Omit<Omit<Pick<DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "key" | keyof HTMLAttributes<...>> & { ...; } & { ...; }, never> & Partial<...>, "theme"> & { ...; } & { ...; }): ReactElement<...>', gave the following error. Type 'string' is not assignable to type 'undefined'.

I found in @types/styled-component that 'as' props can be 'never | undefined', and my variantsMap returns string type.
But I really want to use this 'as' prop with my variant-specific html tag selection feature.

Is there any way to solve this problem?

const variantMap = {
  h1: 'h1',
  h2: 'h2',
  h3: 'h3',
  h4: 'h4',
  h5: 'h5',
  h6: 'h6',
  subheading1: 'h6',
  subheading2: 'h6',
  body1: 'p',
  body2: 'p',
};

export const Typography = ({ variant : string }) => { 

      const selectedComponent = variantMap[variant];

      return (<TypographyRoot
        as={selectedComponent}
        variant={variant}
        {...props}
      >
        {children}
      </TypographyRoot>);
}

CodePudding user response:

First of all, export const Typography = ({ variant : string }) => {} is invalid syntax.

You just changed the name of destructured variant to string. You did not provide a type.

The reason you have an error even with valid string type like here export const Typography = ({ variant }:{variant: string}) => {} is that variantMap expects as a key h1 | 'h2' |'h3' ... keys whereas string is much wider. I'd willing to bet that you don't want to assign foo string to variant property.

IN order to fix it, you just need to make variantMap immutable and apply appropriate constraint to variantMap:

import React from 'react'
import styled from "styled-components";

const Div = styled.div`
  color: red;
`;

const VariantMap = {
  h1: 'h1',
  h2: 'h2',
  h3: 'h3',
  h4: 'h4',
  h5: 'h5',
  h6: 'h6',
  subheading1: 'h6',
  subheading2: 'h6',
  body1: 'p',
  body2: 'p',
} as const;

type Props = {
  variant: keyof typeof VariantMap
}
export const Typography = ({ variant }: Props) => {

  const selectedComponent = VariantMap[variant];

  return <Div
    as={selectedComponent}
  />
}

Now styled-component is happy.

Playground

  • Related