skip to Main Content

I am new to React and TypeScript and I created an app with Expo Go that contains the React Bottom Tab Navigator (https://reactnavigation.org/docs/bottom-tab-navigator/). My problem is that the example does not define the types for the {navigation} parameter and I would like to solve this.

Note: it works without defining the type, but I would like to learn and understand how to fix it…

I have a Navigation.tsx containing my root stack and the tabs (I hope I limited it to the relevant parts)

const Stack = createNativeStackNavigator();
   
declare global {
    namespace ReactNavigation {
        interface RootParamList extends RootStackParamList { }
    }
}
    
type RootStackParamList = {
    Tabs: NavigatorScreenParams<RootTabParamList> | undefined;
    Modal: undefined;
    NotFound: undefined;
};

export type RootStackScreenProps<Screen extends keyof RootStackParamList> = NativeStackScreenProps<
    RootStackParamList,
    Screen
>;
    
const BottomTab = createBottomTabNavigator<RootTabParamList>();
    
type RootTabParamList = {
    About: undefined;
    Test: undefined;
};
    
export type RootTabScreenProps<Screen extends keyof RootTabParamList> = CompositeScreenProps<
    BottomTabScreenProps<RootTabParamList, Screen>,
    NativeStackScreenProps<RootStackParamList>
>;
   
export default function Navigation({ colorScheme }: { colorScheme: ColorSchemeName }) {
    return (
        <NavigationContainer
            linking={LinkingConfiguration}
            theme={colorScheme === 'dark' ? DarkTheme : DefaultTheme}
        >
            <Stack.Navigator>
                <Stack.Screen name="Tabs" component={BottomTabNavigator} options={{ headerShown: false }}/>
                // (!) Problem line
                <Stack.Screen name="NotFound" component={NotFoundScreen} options={{ title: 'Oops!' }}/>
                <Stack.Group screenOptions={{ presentation: 'modal' }}>
                    <Stack.Screen name="Modal" component={ModalScreen} />
                </Stack.Group>
            </Stack.Navigator>
        </NavigationContainer>
    );
}

I found out which is the right type for the {navigation} in one of the tabs, e.g. About.tsx:

import React from 'react';
import { StyleSheet, TouchableOpacity } from 'react-native';

import { Text, View } from '../ui/Components';
import { RootTabScreenProps } from '../ui/Navigation';

export default function About({ navigation }: RootTabScreenProps<'About'>) {
  return (
    <View style={styles.container}>
      <Text style={styles.title}>About</Text>
      <View style={styles.separator} lightColor="#eee" darkColor="rgba(255,255,255,0.1)" />
      <Text>/screens/About.tsx</Text>
      <TouchableOpacity onPress={() => navigation.replace('Tabs')}>
        <Text>Go to home screen!</Text>
      </TouchableOpacity>
    </View>
  );
}

But I can’t find the right type for the {navigation} in the root components (no tabs), like NotFound.tsx:

import React from 'react';
import { StyleSheet, TouchableOpacity } from 'react-native';

import { Text, View } from '../ui/Components';

// (!) Problem line
export default function NotFoundScreen({ navigation }: ?????) {
  return (
    <View style={styles.container}>
      <Text style={styles.title}>This screen doesn't exist.</Text>
      <TouchableOpacity onPress={() => navigation.replace('Tabs')} style={styles.link}>
        <Text style={styles.linkText}>Go to home screen!</Text>
      </TouchableOpacity>
    </View>
  );
}

Without the type, I (of course) get an error in NotFound.tsx, that

Binding element ‘navigation’ implicitly has an ‘any’ type.

I tried RootStackScreenProps<'NotFound'>, which will let the error above disappear, but instead I get an error in Navigation.tsx on line <Stack.Screen name="NotFound" component={NotFoundScreen} options={{ title: 'Oops!' }}/>:

Type ‘({ navigation }: RootStackScreenProps<"NotFound">) => Element’ is not assignable to type ‘ScreenComponentType<ParamListBase, "NotFound"> | undefined’.
Type ‘({ navigation }: RootStackScreenProps<"NotFound">) => Element’ is not assignable to type ‘FunctionComponent<{}>’.
Types of parameters ‘__0’ and ‘props’ are incompatible.
Type ‘{}’ is missing the following properties from type ‘RootStackScreenProps<"NotFound">’: navigation, route

So… What is the right type to use here?

2

Answers


  1. Chosen as BEST ANSWER

    Thanks to the help of @phantomspooks and his answer (https://stackoverflow.com/a/76248413/4090157), I think I now understand the types a bit better.

    I was finally able to resolve my problem by making the following changes:

    1. Remove <Screen> from export type RootStackScreenProps in Navigation.tsx
    export type RootStackScreenProps = NativeStackScreenProps<
        RootStackParamList
    >;
    
    1. Use that type in NotFound.tsx
    export default function NotFoundScreen({ navigation }: RootStackScreenProps) {
        ...
    }
    

  2. react navigation was written in typescript, so any type you would need relating to it probably exists. Here they cover typing a Stack Navigator, and its same premises for a bottom tab navigator. You just need to export RootStackParamList and import BottomTabScreenProps:

    import React from 'react';
    import { StyleSheet, TouchableOpacity } from 'react-native';
    import { BottomTabScreenProps } from "@react-navigation/bottom-tabs";
    import {RootStackParamList} from '../path/to/Navigator';
    
    import { Text, View } from '../ui/Components';
    // dont need to do this
    //import { RootTabScreenProps } from '../ui/Navigation';
    
    type Props = BottomTabScreenProps<RootStackParamList,"About">
    
    export default function About({ navigation }: Props) {
      return (
        <View style={styles.container}>
          <Text style={styles.title}>About</Text>
          <View style={styles.separator} lightColor="#eee" darkColor="rgba(255,255,255,0.1)" />
          <Text>/screens/About.tsx</Text>
          <TouchableOpacity onPress={() => navigation.replace('Tabs')}>
            <Text>Go to home screen!</Text>
          </TouchableOpacity>
        </View>
      );
    }
    

    And now for doing the NotFound screen:

    import React from 'react';
    import { StyleSheet, TouchableOpacity } from 'react-native';
    import { BottomTabScreenProps } from "@react-navigation/bottom-tabs";
    import {RootStackParamList} from '../path/to/Navigator';
    import { Text, View } from '../ui/Components';
    
    type Props = BottomTabScreenProps<RootStackParamList,"NotFound">
    
    export default function NotFoundScreen({ navigation }: Props) {
      return (
        <View style={styles.container}>
          <Text style={styles.title}>This screen doesn't exist.</Text>
          <TouchableOpacity onPress={() => navigation.replace('Tabs')} style={styles.link}>
            <Text style={styles.linkText}>Go to home screen!</Text>
          </TouchableOpacity>
        </View>
      );
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search