Using React + Typescript, I'd like to create a children prop that only accepts a single child that accepts a understands and accepts a ref attribute. Basically, my children type should accept:
- an instance of a React class component
- a standard React HTML element (e.g.
<button>,<div>) - a
forwardRefwrapped function component
It should not accept:
- a regular function component
- a text node, like a string or number
My goal with this children type is to inject a ref into the child using React.cloneElement with some level of certainty that ref will not go completely ignored.
I have tried
import React from "react";
type Props = {
// EDIT: This is how I originally typed `children` to
// get a decent compile time check that returns errors
// if the children is a text node or multiple elements.
// The same effect can be achieved with the `ReactElement` type.
// children: React.FunctionComponentElement<React.RefAttributes<HTMLElement>>;
children: React.ReactElement;
};
const MyComponent = (props: Props) => {
const childRef = React.useRef(null);
const child = React.Children.only(props.children);
if (!React.isValidElement(child)) {
return props.children;
}
return React.cloneElement(child, {ref: childRef});
};
This compiles, and MyComponent not accept text nodes as children, but it does accept non forwardRef function components without complaint.
// This fails with an error that a text node is not a valid child.
<MyComponent>hello</MyComponent>
// This compiles without issue, but should fail because MyText is not a `forwardRef` component.
const MyText = () => <span>hello</span>;
<MyComponent><MyText/></MyComponent>
I've also tried some other types, but this feels like the closest I've gotten.
Is creating a type requirement like this possible with Typescript? If so, how can it be done?