Reputation: 2750
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.
Upvotes: 24
Views: 21462
Reputation: 283355
I'm doing this (based on Danziger's answer):
type MaybeArray<T> = T[] | T
export type ChildrenOfType<T> = MaybeArray<ReactElement<ComponentPropsWithoutRef<T>>>
Usage:
interface SelectBoxProps extends Pick<React.ComponentPropsWithoutRef<'select'>, 'id' | 'value' | 'onChange' | 'disabled' | 'readOnly' | 'onFocus' | 'onBlur' | 'required' | 'defaultValue' | 'style'> {
children?: ChildrenOfType<'option'>
}
I don't know if it actually does anything though -- my IDE doesn't complain when I throw a <div>
in there but at least it serves as human-documentation.
Upvotes: -2
Reputation: 21191
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:
What you could do instead is declare a prop, let's say links?: IHeaderLinkProps[]
, to pass down the props you need to create those HeaderLink
s, 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>
...
);
};
Upvotes: 18