I'm getting some TypeScript errors when using react-navigation 5. I'm sure this is something I am doing wrong with regards to how I am typing things / structuring the app but I'm not sure what.
I was following these guides on the documentation as a starting point:
https://reactnavigation.org/docs/en/tab-based-navigation.html#a-stack-navigator-for-each-tabhttps://reactnavigation.org/docs/en/typescript.html
I want to have an app structure like this:
- Root Tab Navigator
- Home Stack
- Home Page
- News Detail Page
- Settings Stack
- Settings Page
- Home Stack
Everything is working fine when it runs but I'm getting some type of errors when trying to navigate from a page inside one stack to a page in another stack e.g navigating from News Detail Page to Settings page.
This is the code I currently have.
import * as React from 'react';
import { Button, Text, View } from 'react-native';
import { createStackNavigator, StackNavigationProp } from '@react-navigation/stack';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { RouteProp } from '@react-navigation/core';
enum AppRoute {
// Home Stack
HOME = 'HOME',
NEWS_DETAILS = 'NEWS_DETAILS',
// Settings Stack
SETTINGS = 'SETTINGS',
}
type HomeStackParamList = {
[AppRoute.HOME]: undefined;
[AppRoute.NEWS_DETAILS]: {
id: number;
};
}
type SettingsStackParamList = {
[AppRoute.SETTINGS]: undefined;
}
type HomeProps = {
navigation: StackNavigationProp<
HomeStackParamList,
AppRoute.HOME
>;
route: RouteProp<HomeStackParamList, AppRoute.HOME>;
}
type NewsDetailsProps = {
navigation: StackNavigationProp<
HomeStackParamList,
AppRoute.NEWS_DETAILS
>;
route: RouteProp<HomeStackParamList, AppRoute.NEWS_DETAILS>;
}
type SettingsProps = {
navigation: StackNavigationProp<
SettingsStackParamList,
AppRoute.SETTINGS
>;
route: RouteProp<SettingsStackParamList, AppRoute.SETTINGS>;
}
// ----------------------------------------------------------------------------
function HomeScreen(props: HomeProps) {
const { navigation } = props;
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Home screen</Text>
<Button
title="Go to Details"
onPress={() => navigation.navigate(AppRoute.NEWS_DETAILS, { id: 1 })}
/>
<Button
title="Go to Settings"
onPress={() => navigation.navigate(AppRoute.SETTINGS)}
/>
</View>
);
}
function DetailsScreen(props: NewsDetailsProps) {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Details! {props.route.params.id}</Text>
</View>
);
}
function SettingsScreen(props: SettingsProps) {
const { navigation } = props;
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Settings screen</Text>
<Button
title="Go to Home -> Details"
onPress={() => navigation.navigate(AppRoute.NEWS_DETAILS, { id: 2 })}
/>
</View>
);
}
const HomeStack = createStackNavigator<HomeStackParamList>();
function HomeStackScreen() {
return (
<HomeStack.Navigator>
<HomeStack.Screen name={AppRoute.HOME} component={HomeScreen} />
<HomeStack.Screen name={AppRoute.NEWS_DETAILS} component={DetailsScreen} />
</HomeStack.Navigator>
);
}
const SettingsStack = createStackNavigator<SettingsStackParamList>();
function SettingsStackScreen() {
return (
<SettingsStack.Navigator>
<SettingsStack.Screen name={AppRoute.SETTINGS} component={SettingsScreen} />
</SettingsStack.Navigator>
);
}
const Tab = createBottomTabNavigator();
type TabNavigatorProps = React.ComponentProps<typeof Tab.Navigator>;
export const AppNavigator = (props: Partial<TabNavigatorProps>): React.ReactElement => {
return (
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeStackScreen} />
<Tab.Screen name="Settings" component={SettingsStackScreen} />
</Tab.Navigator>
);
}
This is the error I'm getting:
No overload matches this call.
Argument of type '[AppRoute.SETTINGS]' is not assignable to parameter of type '[AppRoute.HOME | AppRoute.NEWS_DETAILS] | [AppRoute.HOME | AppRoute.NEWS_DETAILS, { id: number; }]'.
Type '[AppRoute.SETTINGS]' is not assignable to type '[AppRoute.HOME | AppRoute.NEWS_DETAILS]'.
Type 'AppRoute.SETTINGS' is not assignable to type 'AppRoute.HOME | AppRoute.NEWS_DETAILS'.
Overload 2 of 2, '(route: { key: string; params?: { id: number; }; } | { name: AppRoute.HOME | AppRoute.NEWS_DETAILS; key?: string; params: { id: number; }; }): void', gave the following error.
Argument of type 'AppRoute.SETTINGS' is not assignable to parameter of type '{ key: string; params?: { id: number; }; } | { name: AppRoute.HOME | AppRoute.NEWS_DETAILS; key?: string; params: { id: number; }; }'.ts(2769)
I think I understand why this is happening, it's because HomeStackParamList doesn't have the settings route defined in it. Should I create a union type of every stacks page params and use that everywhere so I don't have this error or is there a better way to structure this?
I'm thinking I could possibly just create a union type of all the param lists like this but I'm not sure if that is the correct way or not? Something like this:
type HomeStackParamList = {
[AppRoute.HOME]: undefined;
[AppRoute.HOME_DETAILS]: {
id: number;
};
}
type SettingsStackParamList = {
[AppRoute.SETTINGS]: undefined;
}
type AppStackParamList = HomeStackParamList & SettingsStackParamList;
type HomeProps = {
navigation: StackNavigationProp<
AppStackParamList,
AppRoute.HOME
>;
route: RouteProp<AppStackParamList, AppRoute.HOME>;
}
type DetailsProps = {
navigation: StackNavigationProp<
AppStackParamList,
AppRoute.HOME_DETAILS
>;
route: RouteProp<AppStackParamList, AppRoute.HOME_DETAILS>;
}
type SettingsProps = {
navigation: StackNavigationProp<
AppStackParamList,
AppRoute.SETTINGS
>;
route: RouteProp<AppStackParamList, AppRoute.SETTINGS>;
}
Any help advice to point me in the right direction would be greatly appreciated, thank you