Typescript(ing) React.cloneElement or how to type a child element with props injected by the parent
This article assumes you are an experienced developer in React and Typescript.
Why?
After spending a lot of time on Stack Overflow and other websites, I found no solution to the problem in the title. So, I decided to share my own findings.
We want to use React.cloneElement to create a new component which is received as a child (using props.chidren) and to pass to it a new prop.
The problem
The idea is to pass the prop gift, from the Parent to the Child component. Giving this scenario, we have the following code, which works, does the job, but has a Typescript error.
The problem with it is that Typescript expects to receive the prop gift on the Child element, as we created the definition, but we do not give it, because it is injected inside the Parent component:
We may try to pass the prop gift inside the Children, however, it is not what we want from the component, as the property should be injected by the Parent.
The solution(s)
Here we have 2 solutions. I am in favour of the first one.
Solution 1
We have to alter the code and use the pattern of FaaC where we let the render function to be used by the children. If you want to find more information about the FaaC pattern you can consult this article.
The differences:
- We changed the signature of Parent, to enforce it receives a function which has the desired props
- We no longer use React.cloneElement inside the Parent component, but we call the render function of the Child
- We use a callback function to receive the props and we inject them inside the Child component
This works because Typescript understands that in the callback function we pass the right props.
The drawback of it can be that it introduces a call of a arrow function. I appreciate it does not affect the computation (more about this subject here).
Solution 2
We can force Typescript to type the Child element by inserting a property gift which has the value undefined and the type never.
I personally do not like this approach because:
- Eslint will complain about the rule no-undefined (you can find more info here) — which yes can be suppressed by using /* eslint-disable no-undefined */ at the start of the file, but it may introduce some side effects)
- It is contra-intuitive to assign the property gift the value undefined and then somehow to be used later as something else (in this case as a string)
The pro of this method is that you do not need to modify the code.
Conclusion
In conclusion, I found 2 methods. One method is simple, by just tricking Typescript, the other is more complex, but it offers a clear view of the logic.
Tell me in the comments if you have any other solution.