styled components 'as' prop and custom prop in typescript

Viewed 867

I'm getting an overload typescript warning when using styled components as prop with a custom prop.

Overload 1 of 2, '(props: Omit & Partial>, "theme"> & { ...; } & { ...; }): ReactElement', gave the following error.

 Type 'string' is not assignable to type 'undefined'.

Overload 2 of 2, '(props: StyledComponentPropsWithAs):
ReactElement, string | JSXElementConstructor>', gave the following error.

 Type '{ children: string | undefined; as: "span"; variants: { show: { opacity: number; y: number; transition: { ease: string; duration: number; staggerChildren: number; delayChildren: number; }; }; hide: { ...; }; }; }' is not assignable to type 'IntrinsicAttributes & Omit, HTMLSpanElement>, "key" | keyof HTMLAttributes> & { ...; }, never> & Partial, "theme"> & { ...; } & { ...; }'.

 Property 'variants' does not exist on type 'IntrinsicAttributes & Omit, HTMLSpanElement>, "key" | keyof HTMLAttributes> & { ...; }, never> & Partial, "theme"> & { ...; } & { ...; }'.ts(2769)

index.d.ts(161, 53): The expected type comes from property 'as' which is declared here on type 'IntrinsicAttributes & Omit & Partial>, "theme"> & { ...; } & { ...; }'

Example:

Link component

import React, { FC } from "react";
import styled from "styled-components";
import { motion } from "framer-motion";

interface IProps {
  variants?: any;
}

const Atag= styled(motion.a)`
  text-decoration: none;
`;

const Link: FC<IProps> = (props) => {
  return <Atag{...props} />;
};

export default Link;

Another file where I import in Link.

const StyledLink= styled(Link)`
   border: 1px solid red;
`;

<StyledLink as="button" variants={motionVariant}>hello</StyledLink>

motionVariant is an object for frame motion.

If I remove the variants prop and just use the as prop typescript stops complaining. If I remove the as prop and keep the variants prop it stops complaining. But it will not let me use the two together. Why can't I use both here and still keep typescript happy.

1 Answers

I came across a similar problem, I think due to the way in which generic props need to be defined in styled-components it's a little confusing how to define them when wrapping motion with styled().

how to define props according to styled components docs

import styled from 'styled-components';
import Header from './Header';

interface TitleProps {
  readonly isActive: boolean;
}

const Title = styled.h1<TitleProps>`
  color: ${(props) => (props.isActive ? props.theme.colors.main : props.theme.colors.secondary)};
`;

How to define the props in your code without motion:

import styled from "styled-components";

interface IProps {
  variants?: any;
}

const Link = styled.a<IProps>`
  text-decoration: none;
`;

export default Link;

How to define the props in your code with motion:

import { HTMLMotionProps, motion } from "framer-motion";
import styled from "styled-components";

/**
 * notice the props extend HTMLMotionProps<"a"> this is so all the default   
 * props are passed such as `onClick`
 */
interface IProps extends HTMLMotionProps<"a">{
  variants?: any;
}

//notice motion is called as a function instead of `motion.a`
const Link = styled(motion<IProps>("a"))`
  text-decoration: none;
`;

export default Link;
Related