类型化路由

编辑页面

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


For the complete documentation index, see llms.txt. Use this Use this file to discover all available pages.

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

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

开始使用

快速开始

如果你是按照 Expo Router 快速开始指南 创建的项目,那么项目已经配置好了 typed routes。首次运行 npx expo start 时,Expo CLI 将生成所需的类型文件。之后,每当你在 .tsx 文件中使用 Expo Router 的 <Link> 组件时,都可以为 href 属性使用自动补全。

手动配置

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

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

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

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

类型生成

当开发服务器启动时,Expo Router 中的 typed routes 会自动生成。默认情况下,这些生成的类型会被配置为不受 Git 跟踪,并会添加到本地 .gitignore 文件中。这样可以确保自动生成的文件不会让你的版本控制系统变得杂乱。

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

静态类型路由

使用 Href<T> 的组件和函数现在将具有静态类型,并拥有更严格的定义。例如:

<Link href="/about" /><Link href="/user/1" /><Link href={`/user/${id}`} /><Link href={("/user" + id) as Href} /> // 如果 href 不是有效路由,TypeScript 会报错<Link href="/usser/1" />

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

对于动态路由,Href 必须是对象,并且其参数会被严格类型检查:

<Link href={{ pathname: "/user/[id]", params: { id: 1 }}} /> // href 虽然有效,但它应该是一个带有 params 的 HrefObject,TypeScript 会报错<Link href="/user/[id]" /> // params 包含无效键,TypeScript 会报错<Link href={{ pathname: "/user/[id]", params: { _id: 1 }}} /> // params 包含未知键,TypeScript 会报错<Link href={{ pathname: "/user/[id]", params: { id: 1, id2: 2 }}} />

相对路径

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

<Link href="/about" /> // 不支持相对路径<Link href="./about" />

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

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

你可以通过使用 useSegments() hook 获取当前路由的第一个片段,从而确保跳转到同一个标签页。

button.tsx
import { Link, useSegments } from 'expo-router'; export function Button() { const [ // 这将取决于当前标签页,值可能是 `(feed)` 或 `(search)`。 first, ] = useSegments(); return <Link href={`/${first}/profile`}>Push profile</Link>; }

现在,你可以在 src/app/(feed)/feed.tsxsrc/app/(search)/search.tsx 中使用 <Button />,在保留当前标签页的同时跳转到 ./profile

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

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

命令式导航

你可以使用带类型的 router 对象以命令式方式进行导航:

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

或者使用带类型的 useRouter() hook:

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

路由参数

对于强类型的路由参数,你可以向 useLocalSearchParamsuseGlobalSearchParams hooks 传入完整的 href。例如:

src/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> </> ); }

查询参数

大多数查询参数不会体现在文件系统中,因此无法自动进行类型推导。你可以通过向 useLocalSearchParamsuseGlobalSearchParams hooks 传入泛型,手动为查询参数添加类型。例如:

src/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>; }

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

src/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>; }

对环境所做的更改

启用 typed routes 后,Expo CLI 会在你项目的根目录生成一个被 Git 忽略的 expo-env.d.ts 文件,更新 .gitignore 以忽略新的根目录 expo-env.d.ts 文件,并修改 tsconfig.json 以包含新的 expo-env.d.ts 文件。

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

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

全局类型

启用 typed routes 后,Expo CLI 会向你的项目添加以下全局类型:

  • 设置 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

启用 typed routes 后,Expo CLI 还会增强 react-native 类型以支持 React Native Web。会进行以下更改:

  • ViewStyleTextStyleImageStyle 添加额外的仅 Web 样式
  • TextProps 添加 tabIndexaria-levellang
  • 为 Pressable 的 childrenstyle 状态回调函数添加 hovered
  • 添加 className 元素