类型化路由
编辑
学习如何在 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 来启用它:
{ "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-router 的 useSegments() hooks 来创建复杂的相对路径。考虑以下结构:
app(feed)_layout.tsxfeed.tsxsearch.tsxprofile.tsx(search)profile.tsxcomponentsbutton.tsx您可以确保使用 useSegments() hook 获取当前路由的第一个段落,以推送到相同的标签。
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.tsx 和 app/(search)/search.tsx 中的 <Button /> 来推送 ./profile,同时保留当前标签。
如果您需要特定用途的段落,可以将完整路由传递给 useSegments
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 传递给 useLocalSearchParams 和 useGlobalSearchParams hooks
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> </> ); }
查询参数
大多数查询参数不会在文件系统中表示,因此无法自动类型化。您可以通过将泛型传递给 useLocalSearchParams 和 useGlobalSearchParams hooks 手动类型化查询参数。例如:
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>; }
如果您需要路由和查询参数的组合,请将路由作为第一个泛型,然后是查询参数
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。以下更改已完成:
- 为
ViewStyle、TextStyle、ImageStyle添加额外的仅 Web 样式 - 为
TextProps添加tabIndex、aria-level、lang - 为 Pressable 的
children和style状态回调函数添加hovered - 添加
className元素