类型化路由

编辑

学习如何在 Expo Router 中使用静态类型链接和路由。


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

Expo Router 支持通过 Expo CLI 自动生成 TypeScript 类型。这使得 <Link>hooks API 能够静态类型化。此功能目前处于 beta 状态,默认情况下未启用。

开始

快速开始

如果您按照 Expo Router 快速开始指南 创建了项目,则您的项目已配置为使用类型化路由。Expo CLI 将在您第一次运行 npx expo start 时生成所需的类型文件。然后,每当您在 .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 中,当开发服务器启动时,类型化路由将自动生成。默认情况下,这些生成的类型配置为被 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 }}} /> // TypeScript 错误,因为 href 有效,但它应该是带有参数的 HrefObject<Link href="/user/[id]" /> // TypeScript 错误,因为参数包含无效键<Link href={{ pathname: "/user/[id]", params: { _id: 1 }}} /> // TypeScript 错误,因为参数包含未知键<Link href={{ pathname: "/user/[id]", params: { id: 1, id2: 2 }}} />

相对路径

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

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

您可以利用来自 expo-routeruseSegments() hooks 来创建复杂的相对路径。考虑以下结构:

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

现在,您可以利用 app/(feed)/feed.tsxapp/(search)/search.tsx 中的 <Button /> 来推送 ./profile,同时保留当前标签。

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

button.tsx
import { Link, useSegments } from 'expo-router'; export function useMySegments() { const segments = useSegments<'app/(search)/profile.tsx'>(); // ^? segments = ['app', '(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'); // ... }

路由参数

对于强类型的路由参数,您可以将完整的 href 传递给 useLocalSearchParamsuseGlobalSearchParams hooks

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

查询参数

大多数查询参数不会在文件系统中表示,因此无法自动类型化。您可以通过将泛型传递给 useLocalSearchParamsuseGlobalSearchParams hooks 手动类型化查询参数。例如:

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

如果您需要路由和查询参数的组合,请将路由作为第一个泛型,然后是查询参数

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

环境的变化

当启用类型化路由时,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 绝对不应在任何时候删除或更改。它不应被提交,应被版本控制忽略。

全局类型

当启用类型化路由时,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

启用类型化路由后,Expo CLI 还增强了 react-native 类型以支持 React Native Web。以下更改已完成:

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