Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React + TypeScript only allow children of specific type

I run into a trouble with trying to allow only specific children to be passed to a component.

Here are my components, HeaderLink

import { SFC, ReactElement } from "react";

import "./Header.scss";

export interface IHeaderLinkProps {
  url: string;
  label: string;
}

const HeaderLink: SFC<IHeaderLinkProps> = ({ url, label }): ReactElement => (
  <li className="nav-item">
    <a href={url}>{label}</a>
  </li>
);

export default HeaderLink;

and Header

import { SFC, ReactElement } from "react";
import { IHeaderLinkProps } from "./HeaderLink";

import "./Header.scss";

interface IHeaderProps {
  children: ReactElement<IHeaderLinkProps> | ReactElement<IHeaderLinkProps>[];
}

const Header: SFC<IHeaderProps> = ({ children }): ReactElement => {
  return (
    <header className="header">
      <div className="container">
        <div className="row">
          <div className="col">
            <img
              className="header__logo"
              src={"/logo.png"}
              alt="monqrime-logo"
            />

            <ul className="header__nav">{children}</ul>
          </div>
        </div>
      </div>
    </header>
  );
};

So the problem is that I want to allow only HeaderLink components to be passed as children to the Header, but my current solution still allows me to put everything as a children.

like image 423
JSEvgeny Avatar asked Dec 17 '19 19:12

JSEvgeny


People also ask

What type is children react TypeScript?

Children is a special prop that allows us to pass in any type of element. It could be a number, a string, a boolean, an array of elements or even another component.

How do you use children's property in react?

children is a special prop, automatically passed to every component, that can be used to render the content included between the opening and closing tags when invoking a component. These kinds of components are identified by the official documentation as “boxes”.

What does react children toArray do?

React.Children.toArrayReturns the children opaque data structure as a flat array with keys assigned to each child. Useful if you want to manipulate collections of children in your render methods, especially if you want to reorder or slice this.props.children before passing it down.


1 Answers

I would probably declare IHeaderProps.children as:

children: React.ReactElement<IHeaderProps> | React.ReactElement<IHeaderProps>[];

To account for the possibility of having both a single and multiple children.

In any case, what you want is not possible. See:

  • How do I restrict the type of React Children in TypeScript, using the newly added support in TypeScript 2.3?

  • React Component children typecheck with typescript

What you could do instead is declare a prop, let's say links?: IHeaderLinkProps[], to pass down the props you need to create those HeaderLinks, rather than their JSX, and render them inside Header:

interface IHeaderProps {
  children?: never;
  links?: IHeaderLinkProps[];
}

...

const Header: React.FC<IHeaderProps> = ({ links }) => {
  return (
    ...

    <ul className="header__nav">
      { links.map(link => <HeaderLink key={ link.url } { ...link } />) }
    </ul>

    ...
  );
};
like image 95
Danziger Avatar answered Oct 13 '22 20:10

Danziger