类型化路由

了解如何在 Expo Router 中使用静态类型的链接和路由。


在你的项目中使用 TypeScript 时可用。Expo Router 开箱即支持标准 TypeScript。有关如何设置的更多信息,请参阅TypeScript指南。

Expo Router 支持使用 Expo CLI 自动生成 TypeScript 类型。这使得 <Link>hooks API 可以进行静态类型检查。此功能目前处于测试阶段,默认未启用。

🌐 Expo Router supports generating TypeScript types automatically with Expo CLI. This enables <Link>, and the hooks API to be statically typed. This feature is currently in beta and is not enabled by default.

开始使用

🌐 Get started

快速开始

🌐 Quick start

如果你是使用 Expo Router 快速入门指南 创建你的项目,那么你的项目已经配置为使用类型化路由。第一次运行 npx expo start 时,Expo CLI 会生成所需的类型文件。然后,每当你在 .tsx 文件中使用 Expo Router <Link> 组件时,就可以对 href 属性使用自动补齐功能。

🌐 If you created your project using the Expo Router quick start guide then your project is already configured to use typed routes. The Expo CLI will generate the required type file the first time you run npx expo start. Then, you can use autocomplete for href props whenever you use an Expo Router <Link> component in a .tsx file.

手动配置

🌐 Manual configuration

在该功能处于测试阶段时,你可以通过在 app.json 中将 experiments.typedRoutes 设置为 true 来启用它:

🌐 While the feature is in beta, you can enable it by setting experiments.typedRoutes to true in your app.json:

app.json
{ "expo": { "experiments": { "typedRoutes": true } } }

运行 npx expo customize tsconfig.json 来配置你的 tsconfig.json,以添加所需的 includes 字段。

🌐 Run npx expo customize tsconfig.json to configure your tsconfig.json to add the required includes fields.

然后,通过运行 npx expo start 启动开发服务器。现在你可以在 Expo Router 的 <Link> 组件的 href 属性中使用自动补全功能。

🌐 Then, start the development server by running npx expo start. You can now use autocomplete in the Expo Router <Link> component's href prop.

类型生成

🌐 Type generation

在 Expo Router 中,类型化路由会在开发服务器启动时自动生成。默认情况下,这些生成的类型被配置为不被 Git 跟踪,并会被添加到本地 .gitignore 文件中。这确保了自动生成的文件不会扰乱你的版本控制系统。

🌐 Typed routes in Expo Router are automatically generated when the development server starts. By default, these generated types are configured to be untracked by Git and will be added to the local .gitignore file. This ensures that autogenerated files do not clutter your version control system.

如果你发现自己处于需要生成这些类型而不启动开发服务器的情况,例如在持续集成 (CI) 服务器上进行类型检查时,可以在 CI 上运行命令 npx expo customize tsconfig.json 来实现。

🌐 If you find yourself in a situation where you need to generate these types without initiating the development server, such as during type checking on a Continuous Integration (CI) server. To do this, run the command npx expo customize tsconfig.json on the CI.

静态类型路由

🌐 Statically typed routes

使用 Href<T> 的组件和功能现在将被静态类型化,并且具有更严格的定义。例如:

🌐 Components and functions that use Href<T> will now be statically typed and have a much stricter definition. For example:

<Link href="/about" /><Link href="/user/1" /><Link href={`/user/${id}`} /><Link href={("/user" + id) as Href} /> // TypeScript errors if href is not a valid route<Link href="/usser/1" />

注意expo-router 还提供了一种 Route 类型,它会自动匹配项目中的所有有效路由。

对于动态路由,Href 需要是对象,并且它们的参数类型严格:

🌐 For dynamic routes, Href's need to be objects and their parameters are strictly typed:

<Link href={{ pathname: "/user/[id]", params: { id: 1 }}} /> // TypeScript errors as href is valid, but it should be a HrefObject with params<Link href="/user/[id]" /> // TypeScript errors as params contain invalid keys<Link href={{ pathname: "/user/[id]", params: { _id: 1 }}} /> // TypeScript errors as params contain unknown keys<Link href={{ pathname: "/user/[id]", params: { id: 1, id2: 2 }}} />

相对路径

🌐 Relative paths

静态类型路由不支持相对路径。你需要为所有路由使用绝对路径:

🌐 Statically typed routes do not support relative paths. You'll need to use absolute paths for all routes:

<Link href="/about" /> // Relative paths are not supported<Link href="./about" />

你可以利用 expo-router 提供的 useSegments() 钩子来创建复杂的相对路径。考虑以下结构:

🌐 You can leverage the useSegments() hooks from expo-router to create complex relative paths. Consider the following structure:

app
(feed)
  _layout.tsx
  feed.tsx
  search.tsx
  profile.tsx
(search)
  profile.tsx
components
button.tsx

你可以使用 useSegments() 钩子获取当前路由的第一个段,从而确保你推送到相同的标签页。

🌐 You can ensure that you push to the same tab by using the useSegments() hook to get the first segment of the current route.

button.tsx
import { Link, useSegments } from 'expo-router'; export function Button() { const [ // This will be either `(feed)` or `(search)` depending on the current tab. first, ] = useSegments(); return <Link href={`/${first}/profile`}>Push profile</Link>; }

现在,你可以在 app/(feed)/feed.tsxapp/(search)/search.tsx 中使用 <Button /> 来推动 ./profile,同时保留当前标签页。

🌐 Now, you can leverage <Button /> from both app/(feed)/feed.tsx and app/(search)/search.tsx to push ./profile while preserving the current tab.

如果你需要特定用途的路段,你可以将完整路径传递给 useSegments

🌐 If you require the segments for a specific use, you can pass the full route to useSegments:

button.tsx
import { Link, useSegments } from 'expo-router'; export function useMySegments() { const segments = useSegments<'/(search)/profile'>(); // ^? segments = ['(search)', 'profile'] return segments; }

命令式导航

🌐 Imperative navigation

你可以使用已输入类型的 router 对象进行命令式导航:

🌐 You can use the typed router object to navigate imperatively:

import { router } from 'expo-router'; router.push('/about');

或者使用输入的 useRouter() 钩子:

🌐 Or with the typed useRouter() hook:

import { useRouter } from 'expo-router'; function Page() { const router = useRouter(); router.push('/about'); // ... }

路由

🌐 Route parameters

对于强类型的路由参数,你可以将完整的 href 传递给 useLocalSearchParamsuseGlobalSearchParams 钩子。例如:

🌐 For strongly typed route parameters, you can pass a full href to the useLocalSearchParams and useGlobalSearchParams hooks. For example:

app/search.tsx
import { Text } from 'react-native'; import { useLocalSearchParams } from 'expo-router'; export default function Page() { const { profile, // string search, // string[] } = useLocalSearchParams<'/(search)/[profile]/[...search]'>(); return ( <> <Text>Profile: {profile}</Text> <Text>Search: {search.join(',')}</Text> </> ); }

查询参数

🌐 Query parameters

大多数查询参数不会在文件系统中表示,因此无法自动输入。你可以通过向 useLocalSearchParamsuseGlobalSearchParams 钩子传递泛型来手动输入查询参数。例如:

🌐 Most query parameters will not be represented in the file system and therefore cannot be typed automatically. You can type query parameters manually by passing a generic to the useLocalSearchParams and useGlobalSearchParams hooks. For example:

app/search.tsx
import { Text } from 'react-native'; import { useLocalSearchParams } from 'expo-router'; export default function Page() { const { query } = useLocalSearchParams<{ query?: string }>(); return <Text>Search: {query ?? 'unset'}</Text>; }

如果你需要同时使用路由参数和查询参数,请将路由作为第一个泛型,然后传入查询参数:

🌐 If you need a combination of route and query parameters, pass the route as the first generic and then the query parameters:

app/search.tsx
import { Text } from 'react-native'; import { useLocalSearchParams } from 'expo-router'; export default function Page() { const { query, profile, search } = useLocalSearchParams< '/[profile]/[...search]', { query?: string } >(); return <Text>Search: {query ?? 'unset'}</Text>; }

对环境的改变

🌐 Changes made to the environment

启用类型化路由时,Expo CLI 会在项目的根目录中生成一个被 Git 忽略的 expo-env.d.ts 文件,更新 .gitignore 以忽略新的根目录 expo-env.d.ts 文件,并修改 tsconfig.json 以包含新的 expo-env.d.ts 文件。

🌐 When typed routes is enabled, Expo CLI will generate a git ignored expo-env.d.ts file in your project's root directory, update the .gitignore to ignore the new root expo-env.d.ts file, and modify the tsconfig.json to include the new expo-env.d.ts file.

tsconfig.json 文件中的 includes 字段会被更新,以包含 expo-env.d.ts 文件和一个隐藏的 .expo 目录。这些条目是必需的,不应从文件中删除。

🌐 The includes field in your tsconfig.json gets updated to include expo-env.d.ts and a hidden .expo directory. These entries are required and should not be removed from the file.

生成的 expo-env.d.ts 文件在任何时候都不应被删除或更改。它不应被提交,并且应被版本控制忽略。

🌐 The generated expo-env.d.ts should not be removed or changed at any time. It should not be committed and should be ignored by version control.

全局类型

🌐 Global types

当启用类型化路由时,Expo CLI 会将以下全局类型添加到你的项目中:

🌐 Expo CLI will add the following global types to your project when typed routes is enabled:

  • 设置 process.env.NODE_ENV = "development" | "production" | "test"
  • 允许导入 .[css|sass|scss] 文件
  • *.module.[css|sass|scss] 的导出设置为 Record<string, string>
  • 为 Metro 的 require.context 添加类型。这由 expo/metro-config 启用,并用于静态路由生成。

React Native Web

启用类型化路由后,Expo CLI 还会增强 react-native 类型以支持 React Native Web。所做的更改如下:

🌐 With typed routes enabled, Expo CLI also augments the react-native types to support React Native Web. The following changes are made:

  • ViewStyleTextStyleImageStyle 添加额外的仅限网页样式
  • tabIndexaria-levellang 添加到 TextProps
  • hovered 添加到 Pressable 的 childrenstyle 状态回调函数中
  • 添加 className 元素