React 和 TypeScript 中的 children 类型:完整指南

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

React 和 TypeScript 中 props children 背后隐藏着什么?

\n\n

如果你曾经使用 TypeScript 编写过 React 组件,那么你很可能遇到过这个问题:"props children 的数据类型是什么?"。这个问题是 Stack Overflow 上最热门的问题之一,这并非没有原因。乍一看,答案似乎很简单:ReactNode。但正如俗话所说,细节是魔鬼。

\n\n

在本文中,我们不会仅仅给出一个干巴巴的答案。我们将深入探讨 children 所有可能的类型、它们的区别、最佳实践以及潜在的陷阱。你将了解何时使用 ReactNode,何时使用 ReactElement,以及何时使用更具体的类型。我们开始吧!

\n\n

为什么这个问题如此热门?

\n\n

问题在于,children 是 React 中的一个特殊 prop。它可以是任何东西:

\n\n
    \n
  • 字符串:"你好,世界!"
  • \n
  • 数字:{42}
  • \n
  • JSX 元素:<div>...</div>
  • \n
  • 元素数组:[<span/>, <span/>]
  • \n
  • 函数(render prop):{() => <div/>}
  • \n
  • 甚至是 null、undefined 或 boolean
  • \n
\n\n

由于这种灵活性,新手经常写成 children: any,这完全抵消了 TypeScript 带来的好处。让我们来探讨正确的方法。

\n\n

children 的主要类型

\n\n

1. ReactNode —— 全能选手

\n\n

这是 90% 情况下 children 最常用且最正确的类型。它包含了 React 可以渲染的所有内容。

\n\n
import { ReactNode } from 'react';\n\ninterface CardProps {\n  title: string;\n  children: ReactNode;\n}\n\nfunction Card({ title, children }: CardProps) {\n  return (\n    <div className="card">\n      <h2>{title}</h2>\n      <div className="card-content">\n        {children}\n      </div>\n    </div>\n  );\n}\n\n// 使用方式:\n<Card title="我的卡片">\n  <p>这段文本将被渲染</p>\n  {123} {/* 数字也可以 */}\n  {null} {/* null 也可以 */}\n</Card>
\n\n

ReactNode 包含什么? 实际上它是以下类型的联合:

\n
    \n
  • ReactElement —— JSX 元素
  • \n
  • string 和 number —— 原始类型
  • \n
  • boolean、null、undefined —— 渲染时会被忽略
  • \n
  • ReactNode[] —— 上述类型的数组
  • \n
  • ReactPortal —— 传送门
  • \n
\n\n

2. ReactElement —— 严格模式

\n\n

如果你需要 children 只能是 JSX 元素(不能是字符串或数字),请使用 ReactElement。

\n\n
import { ReactElement } from 'react';\n\ninterface StrictCardProps {\n  children: ReactElement; // 仅限一个 JSX 元素\n}\n\nfunction StrictCard({ children }: StrictCardProps) {\n  return <div className="strict-card">{children}</div>;\n}\n\n// 可行:\n<StrictCard><span>仅限元素</span></StrictCard>\n\n// TypeScript 报错:\n<StrictCard>文本</StrictCard> // ❌ 字符串不符合要求
\n\n

重要提示: ReactElement 要求恰好有一个子元素。如果传递两个元素而没有包裹,TypeScript 也会报错。

\n\n

3. JSX.Element —— ReactElement 的别名

\n\n

JSX.Element 类型实际上就是 ReactElement<any, any>。它不如 ReactNode 灵活,通常不推荐用于 children,除非你在编写库。

\n\n
// 本质上,它们是相同的:\ntype A = JSX.Element;    // ReactElement<any, any>\ntype B = ReactElement;   // Re                

博客

书籍推荐