JSX.Element vs ReactNode vs ReactElement:你必须赢得的类型之战
你好,同行!如果你在用 React 和 TypeScript 编写代码,那么你一定遇到过编译器对返回值类型报错的情况。“Expected JSX.Element, got ReactNode”——这个错误熟悉吗?或者,你可能在泛型中见过 ReactElement,并思考过:“它们到底有什么区别?”
剧透:这不仅仅是学术术语。正确选择类型可以加速你的代码,防止错误,并使组件更可预测。今天,我们将通过实际示例来深入探讨这个主题,让你再也不会混淆这三种类型。开始吧!
快速入门:它们分别是什么
让我们从基础开始。这三种类型都属于同一个生态系统,但抽象程度不同。
ReactElement
这是基本的构建块。ReactElement 是一个包含类型、props 和 key 的对象。它代表了 React 随后将转换为真实 DOM 节点的“蓝图”。如果你编写 React.createElement('div', null, 'Hello'),你得到的就是 ReactElement。
import React from 'react';
const element: React.ReactElement = React.createElement('div', { className: 'box' }, '你好!');// 类型:ReactElement<{ className: string }, 'div'>重要提示: ReactElement 是一个泛型。你可以指定 props 的类型和元素的类型:
type MyElement = React.ReactElement<{ name: string }, 'button'>;JSX.Element
这是ReactElement 的扩展,增加了 JSX 的特性。本质上,JSX.Element 就是 ReactElement<any, any>。当你在 JSX 中编写 <div /> 时,TypeScript 会将其视为 JSX.Element。
const jsxElement: JSX.Element = <div>JSX.Element</div>;与 ReactElement 的主要区别在于 JSX.Element 不是参数化的。它只是“任何 JSX 元素”。
ReactNode
这是最宽泛的类型。ReactNode 包括:
- ReactElement
- 字符串
- 数字
- 布尔值 (true/false)
- null 和 undefined
- 上述类型的数组
- React Portal
简单来说:ReactNode 是 React 可以渲染的所有内容。这个类型正是用于组件中的 children。
type ReactNode = | React.ReactElement | string | number | boolean | null | undefined | React.ReactNode[];何时使用什么?实际场景
现在是最有趣的部分——让我们分析一下在实际情况下应该选择哪种类型。
场景 1:组件的返回值
你的组件返回 JSX。应该指定什么类型?
// ❌ 不好:类型过于宽泛function MyComponent(): ReactNode { return <div>你好</div>;}
// ✅ 好:使用 JSX.Elementfunction MyComponent(): JSX.Element { return <div>你好</div>;}
// ✅ 优秀:如果需要严格的 props 类型化function MyComponent(): React.ReactElement<{ title: string }> { return <div>你好</div>;}为什么? 组件总是返回 JSX(或者 null,如果不渲染任何内容)。ReactNode 过于宽泛——它允许返回字符串、数字或数组,这可能导致意外的错误。
场景 2:组件中的 children
在这里,ReactNode 是你最好的朋友。子元素可以是任何内容:字符串、另一个组件、null(条件渲染)或数组。
interface CardProps { title: string; children: React.ReactNode; // ✅ 正确}
function Card({ title, children }: CardProps): J