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']>();
// ...
}