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

在 TypeScript 中進行類型檢查

React Navigation 使用 TypeScript 編寫,並為 TypeScript 專案匯出類型定義。

類型檢查導航器

若要類型檢查我們的路由名稱和參數,我們需要做的第一件事,就是為「路由名稱」至「路由參數」建立一個物件類型。例如,假設我們的根導航器中有一個名為 個人資料 的路由,其應該有一個參數 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>

這將提供 Navigator 和 Screen 組件屬性的類型檢查和 IntelliSense。

注意事項

包含對應關係的類型必須為類型別名 (例如:type RootStackParamList = { ... })。不能是介面 (例如:interface RootStackParamList { ... })。也不應延伸 ParamListBase (例如:interface RootStackParamList extends ParamListBase { ... })。這麼做會導致不正確的類型檢查,允許傳入不正確的路由名稱。

輸入檢查螢幕

若要輸入檢查我們的螢幕,我們需要註解螢幕接收到的 navigation 屬性和 route 屬性。React Navigation 中的瀏覽器套件匯出一般類型為 corresponding 瀏覽器中的 navigationroute 屬性定義類型。

舉例來說,你可以使用 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 屬性,你可以這麼做

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

這允許我們輸入檢查路由名稱和參數,你透過 navigatepush 等方式瀏覽。當前路由的名稱對於輸入檢查 route.params 中的參數以及呼叫 setParams 時是必要的。

類似地,你可以匯入 @react-navigation/stackStackScreenProps@react-navigation/drawerDrawerScreenProps@react-navigation/bottom-tabsBottomTabScreenProps 等。

然後你可以使用上面定義的 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 prop 的類型,我們需要從 @react-navigation/native 使用 RouteProp 類型

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

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

建議你建立一個區別的 types.tsx 檔案,供你保留類型並在元件檔案中匯入,而非在每個檔案中重複。

巢狀導航

在巢狀導航工具列中檢查類型畫面和參數

你可以導覽至 Nest 導航工具列中的一個畫面,方法是提供巢狀畫面的 screenparams 屬性

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

為了能夠檢查類型,我們需要從包含巢狀導航工具列的畫面中擷取出參數。可以使用 NavigatorScreenParams 工具執行這項操作

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

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

結合導航 prop

當你巢狀導航工具列時,畫面的導航 prop 是多個導航 prop 的組合。例如,如果我們在堆疊內有一個分頁,navigation prop 同時具有 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 個參數,第一個參數是主導航工具列的 prop 類型(我們案例中,擁有此畫面的導航工具列的類型,也就是包含 Profile 畫面的切換導航工具列),第二個參數是次要的導航工具列的 prop 類型(父導航工具列的類型)。主要類型應始終將畫面的路線名稱作為其第二個參數。

對於多個父導航工具列,這個次要類型應予巢狀

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

如果個別註解 navigation prop,你可以改用 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 元件時,它們已經經過類型檢查,你無需進行特殊操作。不過,有時你可能想將選項萃取出成一個獨立的物件,且你可能想對它進行註解。

要註解選項,我們需要從導覽器匯入對應的類型。例如,對於 @react-navigation/stackStackNavigationOptions

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 導航的類型時,我們建議採取一些措施來整理。

  1. 最好建立一個獨立的檔案 (例如 navigation/types.tsx),其中包含與 React 導航相關的類型。
  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']>();

// ...
}