I am getting this weird TypeScript error:
import React from 'react'
type Props = {
children: string
}
const Container = (props: Props) => {
const isNew = true // make an api call...
if (isNew) {
return <NewContainer {...props} />
} else {
return <OldContainer {...props} />
}
}
const NewContainer = ({ children }: Props) => {
const isSpecial = useIsSpecial()
if (!children) {
return null
}
if (!isSpecial) {
return children
}
return <a>{children}</a>
}
const OldContainer = ({ children }: Props) => {
const isSpecial = useIsSpecial()
if (!children) {
return null
}
if (!isSpecial) {
return children
}
return <a>{children}</a>
}
Those get used like this:
<Container>foo</Children>
I then get these typescript error:
'NewContainer' cannot be used as a JSX component.
Its return type 'string | Element' is not a valid JSX element.
Type 'string' is not assignable to type 'Element'.
'OldContainer' cannot be used as a JSX component.
Its return type 'string | Element' is not a valid JSX element.
Type 'string' is not assignable to type 'Element'.
If I remove the if (!isSpecial) return children, and change it to if (!isSpecial) return <span>{children}</span>, it works fine. Why won't it allow me to return a string? How do I fix this in TypeScript?
TypeScript's React types are incorrect and disallow string as a return value of a component type.
A React component is allowed to return the following types (a React Node):
You can easily test this running the snippet below and observing the component is correctly rendered.
function HelloWorld() { return "Hello, World!"; }
ReactDOM.render(React.createElement(HelloWorld), document.getElementById("root"));
<script src="https://unpkg.com/[email protected]/umd/react.production.min.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.production.min.js"></script>
<div id="root"></div>
You can also notice that running ReactDOM.render("Hello, World!", ...) works as well, since the render function of React DOM expects a valid React Node, and string is such.
But you can also use as reference the isNode function of the prop-types package or the Flow's React$Node type definition, to verify what a React Node type accepts, both type definitions have been created by Facebook Meta itself.
TypeScript maintainers don't seem to be interested in fixing this issue, along with the incorrect ReactNode type that wrongly accepts undefined, so you can only resort to workarounds such as wrapping the text into a Fragment, as explained in other comments.
In your case children is a string so you should wrap it with React.Fragment or <></> but if children was element like below it won't throw an error
<Container><p>foo</p></Container>
so just add react fragment
import React from "react";
const Container = (props) => {
const isNew = true; // make an api call...
if (isNew) {
return <NewContainer {...props} />;
} else {
return <OldContainer {...props} />;
}
};
const NewContainer = ({ children }) => {
const isSpecial = true;
if (!children) {
return null;
}
if (!isSpecial) {
return <React.Fragment>{children}</React.Fragment>;
// or return <>{children}</>;
}
return <a>{children}</a>;
};
const OldContainer = ({ children }) => {
const isSpecial = useIsSpecial();
if (!children) {
return null;
}
if (!isSpecial) {
return <React.Fragment>{children}</React.Fragment>;
// or return <>{children}</>;
}
return <a>{children}</a>;
};
const Main = () => (
<Container>
<a>foo</a>
</Container>
);
export default Main;
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