I have a function that looks at the provided children and if a particular element type is found, it adds some properties to it automatically.
The function is called like this:
render () {
const { children, name, className } = this.props;
return (
<div className={className}>
{this.enrichRadioElements(children, name)}
</div>
)
}
and it is implemented like this:
enrichRadioElements = (children: Array<any>, name: string) => (
React.Children.map(children, child => {
if (!React.isValidElement(child)) {
return child;
}
//@ts-ignore
if (child.props.children) {
child = React.cloneElement(child, {
//@ts-ignore
children: this.enrichRadioElements(child.props.children, name)
});
}
if (child.type === Radio) {
return React.cloneElement(child, {
onChange: this.handleFieldChange,
selectedValue: this.state.selectedValue,
name: name
})
}
else {
return child;
}
})
)
The two //@ts-ignore
comments are what I'm trying to get rid of by writing code that will satisfy typescript. If I remove the first one, the error message I see is this:
Property 'children' does not exist on type '{}'.(ts-2339)
How can I properly modify my code so I can remove the //@ts-ignore
comments? I did go to the definition of child.props and I found this:
interface ReactElement<P = any, T extends string | JSXElementConstructor<any> = string | JSXElementConstructor<any>> {
type: T;
props: P;
key: Key | null;
}
which looks to have a 'props' of type any (if I'm reading it correctly), but typescript doesn't recognize the children property.
By invoking them between the opening and closing tags of a JSX element, you can use React children for entering data into a component. The React children prop is an important concept for creating reusable components because it allows components to be constructed together.
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”.
The ReactFragment , which is included in the ReactNode type, includes an empty interface. Due to the way that TypeScript handles excess property checks, this means that the ReactNode type will accept any object except an object literal. For almost all intents and purposes, it is functionally equivalent to an any type.
To pass a function as props in React TypeScript: Define a type for the function property in the component's interface. Define the function in the parent component. Pass the function as a prop to the child component.
The problem is a couple of things. I started by changing children: Array<any>
to children: React.ReactNode
. You already have a check in there to narrow the type from ReactNode to ReactElement. The trick was 1. using the generic type arguments in isValidElement
and 2. using a new variable with a type assignment on it elementChild
rather than dealing with and mutating the child
argument. EnrichedChildren
may need to be updated to match your use case.
interface EnrichedChildren {
onChange(): void
selectedValue: string
name: string
children?: React.ReactNode
}
enrichRadioElements = (children: React.ReactNode, name: string): any =>
React.Children.map(children, child => {
if (!React.isValidElement<EnrichedChildren>(child)) {
return child
}
let elementChild: React.ReactElement<EnrichedChildren> = child
if (child.props.children) {
elementChild = React.cloneElement<EnrichedChildren>(elementChild, {
children: this.enrichRadioElements(elementChild.props.children, name),
})
}
if (elementChild.type === 'Radio') {
return React.cloneElement(elementChild, {
onChange: () => {},
selectedValue: 'value',
name: name,
})
} else {
return elementChild
}
})
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With