如何在TypeScript React组件中返回字符串或JSX元素



我收到这个奇怪的TypeScript错误:

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>
}

这些习惯是这样的:

<Container>foo</Children>

然后我得到这些打字错误:

'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 (!isSpecial) return children,并将其更改为if (!isSpecial) return <span>{children}</span>,它可以正常工作。为什么它不允许我返回字符串?如何在TypeScript中修复此问题?

TypeScript的React类型不正确,不允许string作为组件类型的返回值。

React组件可以返回以下类型(React节点(:

  • null
  • 布尔值
  • 数字
  • 字符串
  • React元素
  • React门户
  • 数组(React Node或未定义(

您可以通过运行下面的代码段并观察组件是否正确渲染来轻松测试这一点。

function HelloWorld() { return "Hello, World!"; }
ReactDOM.render(React.createElement(HelloWorld), document.getElementById("root"));
<script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
<div id="root"></div>

您还可以注意到,运行ReactDOM.render("Hello, World!", ...)也可以,因为ReactDOM的render函数需要一个有效的ReactNode,而string就是这样。

但您也可以使用prop-types包的isNode函数或Flow的React$Node类型定义作为参考,以验证React Node类型接受什么,这两个类型定义都是由FacebookMeta本身创建的。

TypeScript维护人员似乎对修复这个问题不感兴趣,还有错误地接受undefined的不正确的ReactNode类型,所以您只能采用变通方法,如将文本包装成片段,如其他注释中所述。

在您的情况下,children是一个字符串,因此您应该使用React.Frage或<gt<>但如果孩子是下面这样的元素,它不会抛出错误

<Container><p>foo</p></Container>

所以只需添加反应片段

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;

要在TypeScript抱怨的地方取悦它,请用明确指示它想要的类型

return children as JSX.Element;

或用于普通字符串

return "MyTextToRender" as unknown as JSX.Element;

防止

将类型"string"转换为类型"Element"可能是一个错误,因为两种类型彼此都不充分重叠。如果这是有意的,请先将表达式转换为"unknown"。ts(2352(

最新更新