使用 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中设置以下编译器选项:
dom必须包含在 lib中(注意:如果未指定lib选项,则默认包含dom)。- jsx必须设置为有效选项之一。对于大多数应用,
preserve应该就足够了。 如果你要发布一个库,请查阅 jsx 文档 以选择哪个值。
TypeScript 与 React 组件
注意
每个包含 JSX 的文件都必须使用.tsx文件扩展名。这是 TypeScript 特有的扩展名,它告诉 TypeScript 该文件包含 JSX。
使用 TypeScript 编写 React 与使用 JavaScript 编写 React 非常相似。在使用组件时的关键区别在于,你可以为组件的 props 提供类型。这些类型可用于正确性检查,并在编辑器中提供内联文档。
以 MyButton 组件为例,它来自快速入门指南,我们可以添加一个类型来描述按钮的title:
注意
这些沙盒可以处理 TypeScript 代码,但它们不运行类型检查器。这意味着你可以修改 TypeScript 沙盒来学习,但不会得到任何类型错误或警告。要进行类型检查,你可以使用TypeScript Playground或使用功能更全面的在线沙盒。
这种内联语法是为组件提供类型的最简单方式,不过一旦你需要描述多个字段,它可能会变得笨拙。相反,你可以使用 interface 或 type来描述组件的 props:
描述组件 props 的类型可以非常简单,也可以非常复杂,但它们通常应该是一个使用 type 或 interface 描述的对象类型。你可以在 对象类型中了解 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.ReactNode 和 React.ReactElement的类型检查器示例。这个 TypeScript 游乐场中查看
样式属性
在 React 中使用内联样式时,你可以使用React.CSSProperties 来描述传递给 style 属性的对象。此类型是所有可能 CSS 属性的联合类型,是确保你向 style属性传递有效 CSS 属性并在编辑器中获得自动补全的好方法。
深入学习
本指南涵盖了在 React 中使用 TypeScript 的基础知识,但还有很多内容需要学习。文档中的各个 API 页面可能包含关于如何将它们与 TypeScript 一起使用的更深入文档。
我们推荐以下资源:
- TypeScript 手册是 TypeScript 的官方文档,涵盖了大部分关键语言特性。
- TypeScript 发布说明深入介绍了新功能。
- React TypeScript 备忘单是一个社区维护的、关于在 React 中使用 TypeScript 的备忘单,涵盖了许多有用的边缘案例,内容比本文档更广泛。
- TypeScript 社区 Discord是提问和获取 TypeScript 与 React 相关问题帮助的好地方。
