24

I'm creating a Expo managed React Native app with TypeScript and having some problems with React Navigation and TypeScript.

I want to specify the icon for the Bottom Tab Navigator on the Tab.Screen component.

This code works but complains because route.params could be undefined (line 10).

Property 'icon' does not exist on type 'object'

Can I make the icon prop required on initialParams?

I have looked in the documentation without any luck.

const App: React.FC<{}> = () => {
  return (
    <SafeAreaView style={styles.container}>
      <NavigationContainer>
        <Tab.Navigator
          screenOptions={({ route }) => ({
            tabBarIcon: ({ size, color }) => (
              <MaterialIcons
                size={size}
/* ===> */      name={route.params.icon}
                color={color}
              />
            ),
          })}
        >
          <Tab.Screen
            name="EventListView"
            initialParams={{ icon: 'view-agenda' }}
            component={EventListScreen}
          />
          <Tab.Screen
            name="CreateEvent"
            initialParams={{ icon: 'public' }}
            component={SettingsScreen}
          />
        </Tab.Navigator>
      </NavigationContainer>
    </SafeAreaView>
  )
}
Dainel vdM
  • 253
  • 1
  • 2
  • 5

3 Answers3

54

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

  route: RouteProp<{ params: { icon: ICON_TYPE } }, 'params'>

I recently had the same issue, I came up with this, it seems to be working fine.

Robert Sidzinka
  • 700
  • 4
  • 11
11

The right way of doing this would be to define a type with the params, and send that as the type argument when creating the navigator. Something like this:

type TabNavigatorParamList = {
  EventListView: { icon: string }
  CreateEvent: { icon: string }
}

const Tab = createBottomTabNavigator<TabNavigatorParamList>(); //*

* I've assumed your Tab component was a BottomTabNavigator, but the same code would work no matter what kind of create{whatever}Navigator you use.

Just like that your Tab.Navigator and Tab.Screen will have the right type for their route prop.

There's more info in the docs, and more advanced situations like annotating hooks and nested navigators.

Daniel Reina
  • 5,764
  • 1
  • 37
  • 50
11

It is now pretty well explained in the official doc. Working well.

Read the doc: https://reactnavigation.org/docs/typescript/#type-checking-the-navigator

Here a quick look of how it could look like:

import { createStackNavigator } from '@react-navigation/stack';

type RootStackParamList = {
   Home: undefined;
   Profile: { userId: string };
   Feed: { sort: 'latest' | 'top' } | undefined;
};

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>

Then to type check your component:

interface Props extends NativeStackScreenProps<RootStackParamList, 'Profile'> {
    // other props ...
}

const ProfileScreen: React.FC<Props> = ({ route, navigation }) => {
   // ...
}
Pascal Lapointe
  • 151
  • 1
  • 6