I'm trying to create a React component which enforces that its children are plain HTML elements (such as <p>...</p>
) and not React components (such as <SomeComponent/>
)
Here's some code I've tried, to no avail
import React from "react"
interface ITooltipProps {
// Goal is to require a plain HTML child here, i.e. <p>...</p>, and error if we get anything else
// children: React.ReactElement<JSX.IntrinsicElements[keyof JSX.IntrinsicElements]>;
// This doesn't work because the JSX.IntrinsicElements map just returns props, not the element type
// children: JSX.IntrinsicElements[keyof JSX.IntrinsicElements];
// This doesn't work because it's just asking for a react component that can take props of an HTML element (all of which are optional)
children: React.ReactElement<JSX.IntrinsicElements[keyof JSX.IntrinsicElements]>;
}
export function Tooltip({
children
}: ITooltipProps) {
return (
<div>
{children}
</div>
)
}
// An element which doesn't return a plain HTML child
const F = () => {
return (
<>
<p>Hello</p>
<p>World</p>
</>
)
}
const x = (
// Expect <F/> to error, since F isn't returning a plain HTML object / is returning a fragment
<Tooltip>
<F/>
</Tooltip>
)
(Here's a link to a TS playground with the above code)
Am wondering if anybody knows how to enforce that a component's children are "primitive" HTML elements, as opposed to react components – thanks!
This is not possible.
Rendered JSX has a return type of JSX.Element
no matter what it contains. So plain html, a fragment, or a rendered component all have the same type. Which means you can't use types to constrain it in this way.