跳至主要内容
版本:6.x

使用 TypeScript 进行类型检查

React Navigation 使用 TypeScript 编写,并为 TypeScript 项目导出类型定义。

类型检查导航器

要类型检查我们的路由名称和参数,我们需要做的第一件事是创建一个对象类型,其中包含路由名称到路由参数的映射。例如,假设我们在根导航器中有一个名为 Profile 的路由,它应该有一个参数 userId

type RootStackParamList = {
Profile: { userId: string };
};

同样地,我们需要对每条路由执行相同的操作。

type RootStackParamList = {
Home: undefined;
Profile: { userId: string };
Feed: { sort: 'latest' | 'top' } | undefined;
};

指定 undefined 表示路由没有参数。带有 undefined 的联合类型(例如 SomeType | undefined)表示参数是可选的。

定义完映射后,我们需要告诉我们的导航器使用它。为此,我们可以将其作为泛型传递给 createXNavigator 函数。

import { createStackNavigator } from '@react-navigation/stack';

const RootStack = createStackNavigator<RootStackParamList>();

然后我们可以使用它。

<RootStack.Navigator initialRouteName="Home">
<RootStack.Screen name="Home" component={Home} />
<RootStack.Screen
name="Profile"
component={Profile}
initialParams={{ userId: user.id }}
/>
<RootStack.Screen name="Feed" component={Feed} />
</RootStack.Navigator>

这将为 NavigatorScreen 组件的 props 提供类型检查和智能感知。

注意

包含映射的类型必须是类型别名(例如 type RootStackParamList = { ... })。它不能是接口(例如 interface RootStackParamList { ... })。它也不应该扩展 ParamListBase(例如 interface RootStackParamList extends ParamListBase { ... })。这样做会导致错误的类型检查,允许您传递错误的路由名称。

类型检查屏幕

为了类型检查我们的屏幕,我们需要对屏幕接收到的 navigation prop 和 route prop 进行注释。React Navigation 中的导航器包导出泛型类型来定义对应导航器的 navigationroute props 的类型。

例如,您可以对 Native Stack Navigator 使用 NativeStackScreenProps

import type { NativeStackScreenProps } from '@react-navigation/native-stack';

type RootStackParamList = {
Home: undefined;
Profile: { userId: string };
Feed: { sort: 'latest' | 'top' } | undefined;
};

type Props = NativeStackScreenProps<RootStackParamList, 'Profile'>;

该类型接受 3 个泛型

  • 我们之前定义的参数列表对象
  • 屏幕所属路由的名称
  • 导航器的 ID(可选)

如果您的导航器有 id prop,您可以执行以下操作

type Props = NativeStackScreenProps<RootStackParamList, 'Profile', 'MyStack'>;

这使我们能够类型检查使用 navigatepush 等进行导航的路由名称和参数。当前路由的名称对于类型检查 route.params 中的参数以及调用 setParams 时是必要的。

类似地,您可以从 @react-navigation/stack 导入 StackScreenProps,从 @react-navigation/drawer 导入 DrawerScreenProps,从 @react-navigation/bottom-tabs 导入 BottomTabScreenProps 等等。

然后,您可以使用上面定义的 Props 类型来注释您的组件。

对于函数组件

function ProfileScreen({ route, navigation }: Props) {
// ...
}

对于类组件

class ProfileScreen extends React.Component<Props> {
render() {
// ...
}
}

您可以从 Props 类型中获取 navigationroute 的类型,如下所示

type ProfileScreenNavigationProp = Props['navigation'];

type ProfileScreenRouteProp = Props['route'];

或者,您也可以分别注释 navigationroute 属性。

要获取 navigation 属性的类型,我们需要从导航器中导入相应的类型。例如,@react-navigation/native-stackNativeStackNavigationProp

import type { NativeStackNavigationProp } from '@react-navigation/native-stack';

type ProfileScreenNavigationProp = NativeStackNavigationProp<
RootStackParamList,
'Profile'
>;

类似地,您可以从 @react-navigation/stack 中导入 StackNavigationProp,从 @react-navigation/drawer 中导入 DrawerNavigationProp,从 @react-navigation/bottom-tabs 中导入 BottomTabNavigationProp 等。

要获取 route 属性的类型,我们需要使用 @react-navigation/native 中的 RouteProp 类型

import type { RouteProp } from '@react-navigation/native';

type ProfileScreenRouteProp = RouteProp<RootStackParamList, 'Profile'>;

我们建议您创建一个单独的 types.tsx 文件,在其中保存类型并在您的组件文件中导入它们,而不是在每个文件中重复它们。

嵌套导航器

在嵌套导航器中类型检查屏幕和参数

您可以导航到嵌套导航器中的屏幕,方法是为嵌套屏幕传递 screenparams 属性

navigation.navigate('Home', {
screen: 'Feed',
params: { sort: 'latest' },
});

为了能够对它进行类型检查,我们需要从包含嵌套导航器的屏幕中提取参数。这可以使用 NavigatorScreenParams 实用程序完成

import { NavigatorScreenParams } from '@react-navigation/native';

type TabParamList = {
Home: NavigatorScreenParams<StackParamList>;
Profile: { userId: string };
};

组合导航属性

当您嵌套导航器时,屏幕的导航属性是多个导航属性的组合。例如,如果我们在堆栈中有一个选项卡,则 navigation 属性将同时具有 jumpTo(来自选项卡导航器)和 push(来自堆栈导航器)。为了更轻松地组合来自多个导航器的类型,您可以使用 CompositeScreenProps 类型

import type { CompositeScreenProps } from '@react-navigation/native';
import type { BottomTabScreenProps } from '@react-navigation/bottom-tabs';
import type { StackScreenProps } from '@react-navigation/stack';

type ProfileScreenProps = CompositeScreenProps<
BottomTabScreenProps<TabParamList, 'Profile'>,
StackScreenProps<StackParamList>
>;

CompositeScreenProps 类型接受 2 个参数,第一个参数是主要导航的属性类型(拥有此屏幕的导航器的类型,在本例中是包含 Profile 屏幕的选项卡导航器),第二个参数是次要导航的属性类型(父导航器的类型)。主要类型应始终以屏幕的路由名称作为其第二个参数。

对于多个父导航器,此辅助类型应嵌套。

type ProfileScreenProps = CompositeScreenProps<
BottomTabScreenProps<TabParamList, 'Profile'>,
CompositeScreenProps<
StackScreenProps<StackParamList>,
DrawerScreenProps<DrawerParamList>
>
>;

如果单独注释 navigation 属性,可以使用 CompositeNavigationProp 代替。用法类似于 CompositeScreenProps

import type { CompositeNavigationProp } from '@react-navigation/native';
import type { BottomTabNavigationProp } from '@react-navigation/bottom-tabs';
import type { StackNavigationProp } from '@react-navigation/stack';

type ProfileScreenNavigationProp = CompositeNavigationProp<
BottomTabNavigationProp<TabParamList, 'Profile'>,
StackNavigationProp<StackParamList>
>;

注释 useNavigation

危险

注释 useNavigation 不是类型安全的,因为类型参数无法静态验证。建议改为 指定默认类型

要注释从 useNavigation 获取的 navigation 属性,可以使用类型参数。

const navigation = useNavigation<ProfileScreenNavigationProp>();

注释 useRoute

危险

注释 useRoute 不是类型安全的,因为类型参数无法静态验证。建议尽可能使用 route 属性。对于不需要特定路由类型的通用代码,可以使用 useRoute

要注释从 useRoute 获取的 route 属性,可以使用类型参数。

const route = useRoute<ProfileScreenRouteProp>();

注释 optionsscreenOptions

当您将 options 传递给 ScreenscreenOptions 属性传递给 Navigator 组件时,它们已经过类型检查,您无需执行任何特殊操作。但是,有时您可能希望将选项提取到单独的对象中,并且您可能希望对其进行注释。

要注释选项,我们需要从导航器中导入相应的类型。例如,StackNavigationOptions 用于 @react-navigation/stack

import type { StackNavigationOptions } from '@react-navigation/stack';

const options: StackNavigationOptions = {
headerShown: false,
};

类似地,您可以从 @react-navigation/drawer 导入 DrawerNavigationOptions,从 @react-navigation/bottom-tabs 导入 BottomTabNavigationOptions 等。

当使用 optionsscreenOptions 的函数形式时,您可以使用与注释 navigationroute 属性相同的类型注释参数。

注释 NavigationContainer 上的 ref

如果您使用 createNavigationContainerRef() 方法创建 ref,您可以对其进行注释以类型检查导航操作。

import { createNavigationContainerRef } from '@react-navigation/native';

// ...

const navigationRef = createNavigationContainerRef<RootStackParamList>();

类似地,对于 useNavigationContainerRef()

import { useNavigationContainerRef } from '@react-navigation/native';

// ...

const navigationRef = useNavigationContainerRef<RootStackParamList>();

如果您使用的是常规的 ref 对象,您可以为 NavigationContainerRef 类型传递泛型。

使用 React.useRef 钩子的示例

import type { NavigationContainerRef } from '@react-navigation/native';

// ...

const navigationRef =
React.useRef<NavigationContainerRef<RootStackParamList>>(null);

使用 React.createRef 的示例

import type { NavigationContainerRef } from '@react-navigation/native';

// ...

const navigationRef =
React.createRef<NavigationContainerRef<RootStackParamList>>();

您可以为根导航器指定一个全局类型,而不是手动注释这些 API,该类型将用作默认类型。

为此,您可以在代码库中的某个位置添加此代码段

declare global {
namespace ReactNavigation {
interface RootParamList extends RootStackParamList {}
}
}

RootParamList 接口让 React Navigation 了解您的根导航器接受的参数。在这里,我们扩展了类型 RootStackParamList,因为这是我们根部堆栈导航器的参数类型。此类型的名称并不重要。

如果您在应用程序中大量使用 useNavigationLink 等,指定此类型很重要,因为它将确保类型安全。它还将确保您在 linking 属性上具有正确的嵌套。

组织类型

在为 React Navigation 编写类型时,我们建议您做几件事以保持井井有条。

  1. 最好创建一个单独的文件(例如 navigation/types.tsx),其中包含与 React Navigation 相关的类型。
  2. 最好创建一个可以重复使用的辅助类型,而不是在组件中直接使用 CompositeNavigationProp
  3. 为根导航器指定一个全局类型可以避免在许多地方进行手动注释。

考虑到这些建议,包含类型的文件可能看起来像这样

import type {
CompositeScreenProps,
NavigatorScreenParams,
} from '@react-navigation/native';
import type { StackScreenProps } from '@react-navigation/stack';
import type { BottomTabScreenProps } from '@react-navigation/bottom-tabs';

export type RootStackParamList = {
Home: NavigatorScreenParams<HomeTabParamList>;
PostDetails: { id: string };
NotFound: undefined;
};

export type RootStackScreenProps<T extends keyof RootStackParamList> =
StackScreenProps<RootStackParamList, T>;

export type HomeTabParamList = {
Popular: undefined;
Latest: undefined;
};

export type HomeTabScreenProps<T extends keyof HomeTabParamList> =
CompositeScreenProps<
BottomTabScreenProps<HomeTabParamList, T>,
RootStackScreenProps<keyof RootStackParamList>
>;

declare global {
namespace ReactNavigation {
interface RootParamList extends RootStackParamList {}
}
}

现在,在注释组件时,您可以编写

import type { HomeTabScreenProps } from './navigation/types';

function PopularScreen({ navigation, route }: HomeTabScreenProps<'Popular'>) {
// ...
}

如果您使用的是 useRoute 等钩子,您可以编写

import type { HomeTabScreenProps } from './navigation/types';

function PopularScreen() {
const route = useRoute<HomeTabScreenProps<'Popular'>['route']>();

// ...
}