Skip to content
Advertisement

How to specify the type for the return of useNavigation

I’m having some diffuculties specifing the parameters for a function (onError) that receive the return of a useNavigation())

import { BottomTabScreenProps } from '@react-navigation/bottom-tabs'
import { CompositeScreenProps, NavigatorScreenParams, useNavigation } from '@react-navigation/native'
import { NativeStackNavigationProp, NativeStackScreenProps } from '@react-navigation/native-stack'
import { AxiosError } from 'axios'

declare global {
  namespace ReactNavigation {
    interface RootParamList extends RootStackParamList {}
  }
}

export type RootStackParamList = {
  SignIn: undefined
  ResetPassword: { email: string }
  Tabs: NavigatorScreenParams<TabsParamList> | undefined
  Modal: undefined
  Error: { error: AxiosError | string } | undefined
}

export type TabsParamList = {
  Overview: NavigatorScreenParams<OverviewParamList> | undefined
  Allocation: undefined
  Menu: NavigatorScreenParams<MenuParamList> | undefined
}

export type OverviewParamList = {
  OverviewRoot: undefined
  Deposit: undefined
}

export type MenuParamList = {
  MenuRoot: undefined

  UserProfile: undefined
  // other settings
}

export type RouteName = keyof RootStackParamList | keyof OverviewParamList | keyof TabsParamList | keyof MenuParamList

export const routeTypeGuard = (route: RouteName) => route as keyof RootStackParamList

export type RootStackScreenProps<Screen extends keyof RootStackParamList> = NativeStackScreenProps<
  RootStackParamList,
  Screen
>

export type RootTabScreenProps<Screen extends keyof TabsParamList> = CompositeScreenProps<
  BottomTabScreenProps<TabsParamList, Screen>,
  NativeStackScreenProps<RootStackParamList>
>


export function useErrorHandling() {
  const navigation = useNavigation()
  // navigation.replace() // this throws a type error - which seems right

  return { onError: (error: unknown) => onError(navigation, error) } // how to avoid this to throw an error ??
}

export function onError(
  nav: ReturnType<typeof useNavigation<NativeStackNavigationProp<RootStackParamList>>>,
  error: unknown
) {
  // nav.replace // does not throw type error, but experienced it to make the app crash because nav.replace was undefined
  nav.navigate('Error', { error: error instanceof Error ? error.message : 'Unknow error' })
}

TS playground

Advertisement

Answer

I have not personally worked with @react-navigation, so I’m going by the types only, but it looks like useNavigation, when called without type parameters, defaults to ReactNavigation.RootParamList which doesn’t match your RootStackParamList. The non-extistent replace method comes from the use of NativeStackNavigationProp rather than the default NavigationProp.

If you specify type parameters RootStackParamList and keyof RootStackParamList for useNavigation and replace NativeStackNavigationProp with NavigationProp (from '@react-navigation/core'), it type checks correctly:

export function useErrorHandling() {
  const navigation =
    useNavigation<NavigationProp<RootStackParamList, keyof RootStackParamList>>()

  // navigation.replace() // Error: Property 'replace' does not exist on type ..

  return { onError: (error: unknown) => onError(navigation, error) }
}

Additionally, you could simplify the nav argument type for onError a little bit by using NavigationProp<RootStackParamList, keyof RootStackParamList> instead of the ReturnType application:

export function onError(
  nav: NavigationProp<RootStackParamList, keyof RootStackParamList>,
  error: unknown
) {
  nav.navigate('Error', { error: error instanceof Error ? error.message : 'Unknow error' })
}

TypeScript playground

User contributions licensed under: CC BY-SA
10 People found this is helpful
Advertisement