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>
これにより、NavigatorおよびScreenコンポーネントのプロパティに対して型チェックとIntelliSenseが提供されます。
マッピングを含む型は、型エイリアス(例:type RootStackParamList = { ... })である必要があります。インターフェース(例:interface RootStackParamList { ... })であってはなりません。また、ParamListBaseを拡張しないでください(例:interface RootStackParamList extends ParamListBase { ... })。そうすると、誤ったルート名を渡すことができる不正確な型チェックが発生します。
画面の型チェック
画面の型チェックを行うには、画面で受信するnavigationプロップとrouteプロップに注釈を付ける必要があります。React Navigationのナビゲーターパッケージは、対応するナビゲーターからのnavigationプロップとrouteプロップの両方の型を定義するためのジェネリック型をエクスポートします。
たとえば、ネイティブスタックナビゲーターには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'>;
これにより、navigate、pushなどを使用してナビゲートしているルート名とパラメーターを型チェックできます。現在のルートの名前は、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型からnavigationとrouteの型を取得できます。
type ProfileScreenNavigationProp = Props['navigation'];
type ProfileScreenRouteProp = Props['route'];
または、navigationプロップとrouteプロップを個別に注釈することもできます。
navigationプロップの型を取得するには、ナビゲーターから対応する型をインポートする必要があります。たとえば、@react-navigation/native-stackの場合はNativeStackNavigationPropです。
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ファイルを作成することをお勧めします。
ナビゲーターのネスト
ネストされたナビゲーターでの画面とパラメーターの型チェック
ネストされた画面のscreenプロパティとparamsプロパティを渡すことで、ネストされたナビゲーターの画面にナビゲートできます。
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画面を含むタブナビゲーター)で、2番目のパラメーターはセカンダリナビゲーションのプロップの型(親ナビゲーターの型)です。プライマリ型には、常に画面のルート名を2番目のパラメーターとして持つ必要があります。
複数の親ナビゲーターの場合、このセカンダリ型はネストされる必要があります。
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>();
optionsとscreenOptionsの注釈
optionsをScreenに渡すか、screenOptionsプロップをNavigatorコンポーネントに渡すと、それらは既に型チェックされているため、特別なことをする必要はありません。ただし、場合によってはオプションを個別のオブジェクトに抽出して、それに注釈を付けたい場合があります。
オプションに注釈を付けるには、ナビゲーターから対応する型をインポートする必要があります。たとえば、@react-navigation/stackの場合はStackNavigationOptionsです。
import type { StackNavigationOptions } from '@react-navigation/stack';
const options: StackNavigationOptions = {
headerShown: false,
};
同様に、@react-navigation/drawerの場合はDrawerNavigationOptions、@react-navigation/bottom-tabsの場合はBottomTabNavigationOptionsなどをインポートできます。
optionsとscreenOptionsの関数形式を使用する場合は、navigationプロップとrouteプロップに注釈を付けるために使用したのと同じ型で引数に注釈を付けることができます。
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>>();
useNavigation、Link、refなどのデフォルトの型の指定
これらのAPIに手動で注釈を付ける代わりに、ルートナビゲーターのグローバル型を指定して、デフォルトの型として使用できます。
これを行うには、コードベースのどこかにこのスニペットを追加できます。
declare global {
namespace ReactNavigation {
interface RootParamList extends RootStackParamList {}
}
}
RootParamListインターフェースは、React Navigationにルートナビゲーターが受け入れるパラメーターを知らせます。ここでは、型RootStackParamListを拡張しています。これは、ルートにあるスタックナビゲーターのパラメーターの型であるためです。この型の名前は重要ではありません。
この型を指定することは、アプリでuseNavigationやLinkなどを多用する場合、型安全性を確保するために重要です。また、linkingプロパティの正しいネストも保証されます。
型の整理
React Navigationの型を記述する際、整理された状態を保つために推奨する点がいくつかあります。
- React Navigation関連の型をまとめた別のファイル(例:
navigation/types.tsx)を作成することをお勧めします。 - コンポーネント内で直接
CompositeNavigationPropを使用する代わりに、再利用可能なヘルパー型を作成する方が良いでしょう。 - ルートナビゲーターのグローバル型を指定すると、多くの場所で手動でアノテーションする必要がなくなります。
これらの推奨事項を考慮すると、型を記述するファイルは次のようになる可能性があります。
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']>();
// ...
}