v19.2Latest

使用 TypeScript

TypeScript 是一种流行的为 JavaScript 代码库添加类型定义的方式。开箱即用,TypeScript支持 JSX,并且你可以通过向项目中添加 @types/react@types/react-dom来获得完整的 React Web 支持。

安装

所有生产级 React 框架都支持使用 TypeScript。请遵循特定框架的安装指南:

向现有 React 项目添加 TypeScript

要安装最新版本的 React 类型定义:

npm install --save-dev @types/react @types/react-dom

需要在你的 tsconfig.json中设置以下编译器选项:

  1. dom 必须包含在 lib中(注意:如果未指定lib选项,则默认包含dom)。
  2. jsx必须设置为有效选项之一。对于大多数应用,preserve 应该就足够了。 如果你要发布一个库,请查阅 jsx 文档 以选择哪个值。

TypeScript 与 React 组件

注意

每个包含 JSX 的文件都必须使用.tsx文件扩展名。这是 TypeScript 特有的扩展名,它告诉 TypeScript 该文件包含 JSX。

使用 TypeScript 编写 React 与使用 JavaScript 编写 React 非常相似。在使用组件时的关键区别在于,你可以为组件的 props 提供类型。这些类型可用于正确性检查,并在编辑器中提供内联文档。

MyButton 组件为例,它来自快速入门指南,我们可以添加一个类型来描述按钮的title

注意

这些沙盒可以处理 TypeScript 代码,但它们不运行类型检查器。这意味着你可以修改 TypeScript 沙盒来学习,但不会得到任何类型错误或警告。要进行类型检查,你可以使用TypeScript Playground或使用功能更全面的在线沙盒。

这种内联语法是为组件提供类型的最简单方式,不过一旦你需要描述多个字段,它可能会变得笨拙。相反,你可以使用 interfacetype来描述组件的 props:

描述组件 props 的类型可以非常简单,也可以非常复杂,但它们通常应该是一个使用 typeinterface 描述的对象类型。你可以在 对象类型中了解 TypeScript 如何描述对象,但你可能也会对使用联合类型来描述可以是几种不同类型之一的 prop,以及从类型创建类型指南中更高级的用例感兴趣。

Hook 示例

来自 @types/react的类型定义包含了内置 Hook 的类型,因此你可以在组件中使用它们而无需任何额外设置。它们的设计考虑到了你在组件中编写的代码,因此大多数时候你会获得推断类型,理想情况下不需要处理提供类型的细节。

不过,我们可以看几个如何为 Hook 提供类型的示例。

useState

useState Hook 会重用作为初始状态传入的值来确定值的类型。例如:

这将为 enabled分配boolean类型,而setEnabled 将是一个接受 boolean 参数或返回 boolean 的函数的函数。如果你想为状态显式提供一个类型,可以通过向 useState调用提供类型参数来实现:

在这个例子中,这样做没有太大用处,但一个常见的情况是当你有一个联合类型时,你可能想要提供一个类型。例如,这里的status可以是几个不同的字符串之一:

或者,正如 状态结构原则中所建议的,你可以将相关的状态分组为一个对象,并通过对象类型描述不同的可能性:

useReducer

useReducer Hook是一个更复杂的 Hook,它接受一个 reducer 函数和一个初始状态。reducer 函数的类型是从初始状态推断出来的。你可以选择性地向useReducer调用提供类型参数来为状态提供类型,但通常更好的做法是在初始状态上设置类型:

我们在几个关键地方使用了 TypeScript:

  • interface State描述了 reducer 状态的形状。
  • type CounterAction描述了可以分派给 reducer 的不同操作。
  • const initialState: State 为初始状态提供了类型,同时也是 useReducer默认使用的类型。
  • stateReducer(state: State, action: CounterAction): State设置了 reducer 函数参数和返回值的类型。

initialState设置类型的一个更明确的替代方案是向useReducer提供类型参数:

useContext

useContext Hook是一种无需通过组件传递 props 即可将数据沿组件树向下传递的技术。它通过创建一个提供者组件,并通常在子组件中创建一个 Hook 来消费该值来实现。

上下文提供的值的类型是从传递给 createContext调用的值推断出来的:

当您有一个合理的默认值时,这种技术是有效的——但偶尔也会遇到没有默认值的情况,在这些情况下,将null 作为默认值可能感觉是合理的。然而,为了让类型系统理解您的代码,您需要在 createContext上显式设置ContextShape | null

这导致了一个问题,即您需要在上下文消费者的类型中消除| null。我们的建议是让 Hook 在运行时检查其是否存在,并在不存在时抛出错误:

useMemo

注意

React Compiler 会自动记忆值和函数,减少手动 useMemo调用的需求。您可以使用编译器来自动处理记忆化。

useMemoHook 将从函数调用中创建/重新访问一个记忆化的值,仅当作为第二个参数传递的依赖项发生变化时才会重新运行该函数。调用 Hook 的结果是从第一个参数中的函数的返回值推断出来的。您可以通过向 Hook 提供类型参数来使其更加明确。

useCallback

注意

React Compiler 会自动记忆值和函数,减少手动 useCallback调用的需求。您可以使用编译器来自动处理记忆化。

useCallback 提供了一个对函数的稳定引用,只要传入第二个参数的依赖项保持不变。与 useMemo类似,函数的类型是从第一个参数中函数的返回值推断出来的,您可以通过向 Hook 提供类型参数来使其更加明确。

在 TypeScript 严格模式下工作时,useCallback 需要为回调中的参数添加类型。这是因为回调的类型是从函数的返回值推断出来的,没有参数就无法完全理解类型。

根据您的代码风格偏好,您可以使用 React 类型中的*EventHandler 函数,在定义回调的同时为事件处理程序提供类型:

实用类型

来自 @types/react包的类型集合相当广泛,当你对 React 和 TypeScript 的交互方式感到得心应手时,值得一读。你可以在DefinitelyTyped 中的 React 文件夹中找到它们。我们将在这里介绍一些更常见的类型。

DOM 事件

在 React 中处理 DOM 事件时,事件的类型通常可以从事件处理程序中推断出来。但是,当你想要提取一个函数并将其传递给事件处理程序时,你需要显式地设置事件的类型。

React 类型中提供了许多事件类型——完整列表可以在这里 找到,它基于 DOM 中最常见的事件

在确定你要查找的类型时,你可以首先查看你正在使用的事件处理程序的悬停信息,这将显示事件的类型。

如果你需要使用未包含在此列表中的事件,可以使用 React.SyntheticEvent类型,它是所有事件的基类型。

子元素

描述组件的子元素有两种常见方法。第一种是使用 React.ReactNode类型,它是可以在 JSX 中作为子元素传递的所有可能类型的联合类型:

这是对子元素非常宽泛的定义。第二种是使用 React.ReactElement类型,它仅指 JSX 元素,而不包括字符串或数字等 JavaScript 原始类型:

请注意,你无法使用 TypeScript 来描述子元素是某种特定类型的 JSX 元素,因此你无法使用类型系统来描述一个只接受<li>子元素的组件。

你可以在 React.ReactNodeReact.ReactElement的类型检查器示例。这个 TypeScript 游乐场中查看

样式属性

在 React 中使用内联样式时,你可以使用React.CSSProperties 来描述传递给 style 属性的对象。此类型是所有可能 CSS 属性的联合类型,是确保你向 style属性传递有效 CSS 属性并在编辑器中获得自动补全的好方法。

深入学习

本指南涵盖了在 React 中使用 TypeScript 的基础知识,但还有很多内容需要学习。文档中的各个 API 页面可能包含关于如何将它们与 TypeScript 一起使用的更深入文档。

我们推荐以下资源: