Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Styled-components React HTML elements factory

I want to create a component handling multiple HTML element with the same properties/logic.

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

interface GridProps {
  htmlElement: 'div' | 'main' | 'header',
}

const GridFactory = (props: GridProps) => {
  switch (props.htmlElement) {
    case 'header':
      return styled.header``;
    case 'main':
      return styled.main``;
    case 'div': default :
      return styled.div``;
  }
}

export const Test = () => (
  <GridFactory htmlElement='div'>
    <p>content...</p>
  </GridFactory>
)

It fails with that error :

Type '{ children: Element; htmlElement: "div"; }' is not assignable to type 'IntrinsicAttributes & GridProps'.
  Property 'children' does not exist on type 'IntrinsicAttributes & GridProps'.

First trick tried

Add an explicit children prop to GridProps :

interface GridProps {
  htmlElement: 'div' | 'main' | 'header',
  children?: React.ReactNode | React.ReactNode[];
}

It gives the corresponding error :

'GridFactory' cannot be used as a JSX component.
  Its return type 'StyledComponent<"header", DefaultTheme, {}, never> | StyledComponent<"div", DefaultTheme, {}, never>' is not a valid JSX element.
    Type 'StyledComponent<"header", DefaultTheme, {}, never>' is not assignable to type 'Element | null'.
      Type 'String & StyledComponentBase<"header", DefaultTheme, {}, never> & NonReactStatics<never, {}>' is missing the following properties from type 'Element': type, props, key

How can I achieve it ?

like image 635
mlisthenewcool Avatar asked Apr 29 '26 10:04

mlisthenewcool


2 Answers

Since your styled.head returns component type rather than React.Element so you can improve your stuff as below:

Specify returned type of your component as stateless functional component React.SFC, you also benefit from this by no need to specify children prop cause it's a part of the type SFC:

const GridFactory: React.SFC<GridProps> = (props) => { ...

Then assign styled component as functional component as well such as Header:

const Header: React.SFC = styled.header``;

To sum up, the full code would be:

interface GridProps {
  htmlElement: 'div' | 'main' | 'header',
}

const Header: React.SFC = styled.header``;

const GridFactory: React.SFC<GridProps> = (props) => {
  switch (props.htmlElement) {
    case 'header':
      return <Header />;

    // More to come

    default: return null
  }
}
like image 121
tmhao2005 Avatar answered May 01 '26 00:05

tmhao2005


The error says that you cannot pass the prop "children" to your component because it doesn't provide that type on the prop interface declaration. In fact, your GridProps interface doesn't declare such property. children is the prop that you "set" when you pass a child component to a parent component, like in your example

export const Test = () => (
  <GridFactory htmlElement='div'>
    <p>content...</p> //This is passed as the children prop of your GridFactory
  </GridFactory>
)

To make typescript stop complaining about it, you should change your interface declaration to

interface GridProps {
  htmlElement: 'div' | 'main' | 'header';
  children: React.Node | React.Node[];
}

Now your Prop type allows you to pass a children prop

like image 37
Rander Gabriel Avatar answered Apr 30 '26 22:04

Rander Gabriel