Using @react-navigation
(6.x) in a React Native app, I've decided to fully annotate our navigation stacks. Everything pretty much works, despite how convoluted RN makes it. The only notable exception is the one I've included below (simplified). In short: when navigating to a stack with parameters, TS complains unless I navigate to a stack inside that stack, i.e. consider the stack itself one of the sub-routes.
Let's consider a top-level StackNavigator (Main
) with a single route (SubStack
):
export type MainParamList = {'SubStack': NavigatorScreenParams<SubStackParamList>}const Stack = createStackNavigator<MainParamList>()export const MainStack = () => { return (<Stack.Navigator><Stack.Screen name='SubStack' component={SubStack} /></Stack.Navigator> )}
Note the usage of NavigatorScreenParams
as explained in the docs, so that the routes inside SubStack
are correctly typed when calling navigation.navigate
. So far so good.
Our second-level of navigation will be SubStack
, which for the purposes of this example is going to contain a single route (SubStackNestedScreen
):
export type SubStackParamList = {'SubStack': { someParam: string }'SubStackNestedScreen': undefined}const Stack = createStackNavigator<SubStackParamList>()type Props = StackScreenProps<SubStackParamList, 'SubStackContainer'>export const SubStack = ({route}: Props) => { // TypeScript will complain here, because someParam doesn't exist in route.params const {someParam} = route.params // Do something with someParam return (<Stack.Navigator><Stack.Screen name='SubStackNestedScreen' component={SomeNestedScreen} /></Stack.Navigator> )}
Considering the above, you'd think that someParam
is defined. Well, if I navigate to SubStack the correct way, then route.params
gets assigned as expected. However, to pass TS compilation, the navigation call I have to use is as follows:
navigation.navigate('MainStack', { screen: 'SubStack', params: { screen: 'SubStack', params: { someParam: 42, } },})
However, the above produces a nested entity when reading route.params
in SubStack
:
console.log(route)// { ... params: { params: { someParam: 42 } }
That means the TS interpretation is wrong compared to reality. When navigating without deep nesting, route.params
is correctly set:
navigation.navigate('MainStack', { screen: 'SubStack', params: { someParam: 42, }})
console.log(route)// { ... params: { someParam: 42 }
I know this is a tricky and long one, but I appreciate all the help. No other nested routes have given me this headache, although possibly because they don't expect any params
.
Thanks a lot <3