Search code examples
typescriptstyled-componentsreact-typescript

React-styledComponents: Types of property 'children' are incompatible


I am trying make Reuse-able style-components cards. I used typescript for my React app. I made children type children?: string | JSX.Element. But in my card container it got undefined and give typescript error: Types of property 'children' are incompatible. even though I made children optional. I Really don't get what is main error and how to fix it.

I share my code in code-sandbox. **Ps: It does not thorw errors in codesandbox**

This is my card component

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

export interface ICardProps {
  title?: string;
  secondaryText?: string;
  hoverable?: boolean;
  thumbnailUrl?: string;
  coverImage?: string;
  url?: string;
  children?: string | JSX.Element;
}

const Container = styled.div<ICardProps>`
  box-shadow: 0px 2px 1px -1px rgba(0, 0, 0, 0.2),
    0px 1px 1px 0px rgba(0, 0, 0, 0.14), 0px 1px 3px 0px;
  border-radius: 6px;
  background: white;
  margin-bottom: 20px;
  &:hover {
    ${({ hoverable }) =>
    hoverable
      ? `box-shadow: 0 0 20px rgba(0, 0, 0, 0.05), 0 0px 60px rgba(0, 0, 0, 0.08)`
      : null}
  }
`;

const Title = styled.h1`
  margin: 0;
  font-size: 24px;
`;
const SecondaryText = styled.p`
  margin: 0;
  color: #6b6b6b;
`;

const Thumbnail = styled.div<ICardProps>`
  width: 70px;
  height: 70px;
  border-radius: 50%;
  background-image: url(${({ url }) => url});
  background-size: cover;
  margin-right: 10px;
`;
const TitleBar = styled.div`
  display: flex;
  align-items: center;
`;

const CoverImage = styled.img`
  width: 100%;
  border-radius: 6px 6px 0 0;
`;
const Content = styled.div`
  padding: 20px;
`;

const ChildrenContent = styled.div`
  background: pink;
`;

const Card = ({
  title,
  secondaryText,
  thumbnailUrl,
  coverImage,
  children,
  hoverable,
  ...props
}: ICardProps) => {
  return (
    <Container
      {...props}
      {...{ hoverable, title, secondaryText, thumbnailUrl, coverImage, ...props }}>
      {coverImage ? <CoverImage src={coverImage} /> : null}
      <Content>
        <TitleBar>
          {thumbnailUrl ? <Thumbnail url={thumbnailUrl} /> : null}
          <div>
            {title ? <Title>{title}</Title> : null}
            {secondaryText ? <SecondaryText>{secondaryText}</SecondaryText> : null}
          </div>
        </TitleBar>
        {typeof children === `string` ?
          <ChildrenContent> {children}</ChildrenContent> :
          children
        }
      </Content>
    </Container>
  )
};

export default Card;

This is how I am trying to use it

import React from "react";
import styled from "styled-components/macro";
import { Card } from "./lib/Card";

const ThinnerCard = styled(Card)`
  width: 400px;
`;
export default function App() {
  return (
    <div>
      <ThinnerCard
        coverImage="http://www.fillmurray.com/500/300"
        thumbnailUrl="http://www.fillmurray.com/100/100"
        title="This is the title"
        secondaryText="Secondary title"
      />

      <Card title="Only a title" secondaryText="And a second text" />

      <Card>
        <h1> Hello I'm a "children" props</h1>
      </Card>
    </div>
  );
}

Solution

  • I found a solution. I extends my typescript props like extendsReact.HTMLAttributes<HTMLDivElement>.

    export interface ICardProps extends React.HTMLAttributes<HTMLDivElement> {
      title?: string;
      secondaryText?: string;
      hoverable?: boolean;
      thumbnailUrl?: string;
      coverImage?: string;
      url?: string;
      style?: React.CSSProperties;
    }
    
    
    
    const Card = ({
      title,
      secondaryText,
      thumbnailUrl,
      coverImage,
      hoverable,
      children, 
      ...props
    }: ICardProps) => {
      return (
        <Container
          {...props}
          {...{
            hoverable,
            title,
            secondaryText,
            thumbnailUrl,
            coverImage,
            ...props
          }}
        >
          {coverImage ? <CoverImage src={coverImage} /> : null}
          <Content>
            <TitleBar>
              {thumbnailUrl ? <Thumbnail url={thumbnailUrl} /> : null}
              <div>
                {title ? <Title>{title}</Title> : null}
                {secondaryText ? (
                  <SecondaryText>{secondaryText}</SecondaryText>
                ) : null}
              </div>
            </TitleBar>
          </Content>
          { children ? (
            <ChildrenContent> {children}</ChildrenContent>
          ) :
            children
          }
        </Container>
      );
    };