Search code examples
javascriptreactjstypescriptstyled-components

How to get StyledComponent's type in intellisense?


I have created a function which takes a component and returns a Styled component of that

import styled from 'styled-components';

const withStyled = (Component, styles) => {
  const StyledComponent = styled(Component)`
    ${styles}
  `;

  return StyledComponent;
};

export default withStyled;

I am using this withStyled function as below

import cx from 'classnames';
import React from 'react';
import withStyled from '../../../hoc/withStyled';
import styles from './Paragraph.style';

interface Props {
  className: string;
  sizeVariant: string;
  children: string;
}

const Paragraph = (props: Props) => {
  const { children, className, ...extraProps } = props;
  return (
    <p className={cx('paragraph', className)} {...extraProps}>
      {children}
    </p>
  );
};

const StyledParagraph = withStyled(Paragraph, styles);

export default StyledParagraph;

But when I am using my Paragraph component, VS code intellisense is not showing me suggestions related to props This is because withStyled function is returning me a new component and my original component's types are lost in the process.

Is there a way to get the original types in StyledComponent too?


Solution

  • TLDR

    function withStyled<
      T extends keyof JSX.IntrinsicElements | React.ComponentType<any>
    >(component: T, styles: string): T {
      return styled(component)`${styles}`;
    }
    

    Stackblitz: https://stackblitz.com/edit/react-ts-rbqxgt?file=App.tsx


    The @types/styled-components package seems to be a bit busted, since the StyledComponent type just resolves to any. This is what I get from Intellisense:

    type StyledComponent<
      C extends keyof JSX.IntrinsicElements | React.ComponentType<any>,
      T extends object,
      O extends object = {},
      A extends string | number | symbol = never
    > = any;
    

    You can use generics to just return the same component type. FC stands for Function Component.

    function withStyled<T>(component: React.FC<T>, styles: string): React.FC<T> {
      return styled(component)`${styles}`;
    }
    

    An arrow function requires you to write <T extends Object> or <T,_> instead of just <T> else it clashes with JSX.

    const withStyled = <T extends Object>(
      Component: React.FC<T>,
      styles: string
    ): React.FC<T> => styled(Component)`${styles}`;
    

    Then, this line:

    const StyledParagraph = withStyled(Paragraph, styles);
    

    Produces this intellisense:

    const StyledParagraph: React.FC<Props>
    

    And you will get type errors as normal.


    To allow for passing any acceptable types, not just function components, you can use T extends keyof JSX.IntrinsicElements | React.ComponentType<any> and just return the same type, as shown in the TLDR.