About
This article will show you how to override/define the type of children of your react component to a specific type in your props.
Step by Step Example
The component to refactor
In this example, we have a layout component that expects only buttons as children to create a button bar.
The base component is as follows where the props accepted are for div element (ie React.HTMLAttributes<HTMLDivElement>)
export function ButtonGroup(props: React.HTMLAttributes<HTMLDivElement>) {
return (
<div className={'btn-group flex-wrap'} {...props}/>
)
}
Step 1: Changing the default children type
The props.children attributes by default have the type React.ReactNode or undefined
To override it to only buttons, you would:
- use the Omit function to exclude the children attribute
- add your children's type definition
Example:
export function ButtonGroup(
props: Omit<React.HTMLAttributes<HTMLDivElement>,"children">
& {
children: HTMLButtonElement[]
// with your button component
// children: (ReactElement<typeof YourButtonComponent>|HTMLButtonElement)[]
}) {
...
}
Step2: Fixing the typescript error
Once you have changed your signature with your type definition, you will get the following typescript error:
TS2322: Type HTMLButtonElement is not assignable to type ReactNode
Type HTMLButtonElement is missing the following properties from type ReactPortal: props, key
index.d.ts(2318, 9): The expected type comes from property children which is declared here on type
DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>
Why? Because the div element in React is a React Node and excepts as children a ReactNode or undefined
It does not like your new definition.
To resolve this problem, you need to override the type by defining it back to a ReactNode (ie children as unknown as React.ReactNode)
Example:
export function ButtonGroup({children, ...props}:
Omit<React.HTMLAttributes<HTMLDivElement>, "children"> &
{
children: HTMLButtonElement[]
}
) {
return (
<div className={'btn-group flex-wrap'} {...props}>
{children as unknown as React.ReactNode}
</div>
)
}
Conclusion
And here you are, you got your typescript component that accepts your type in our case: only buttons.
Note that it adds some complexity and even if you have created a type children component, you may not want to deal with further typescript problem such as
- type. An anchor may also be styled as a button
- performance. Typescript needs to parse and apply narrower restrictions.
And leave the restriction to the developer.