Sithis
Sithis

Reputation: 41

How to Union Type react navigation prop?

Union Type react navigation prop?

Hello everyone,

I am using typescript with react-navigation module on react native and I am trying to type a child component Navigation prop that can be used by two different navigations.

My current Navigation structure is a main tab navigator composed with two stack navigator, and screen from both navigator are using the child component that, onPress navigate to MovieDetailsScreen:

├── tabs
│  ├── SearchStack   ├── SearchScreen
│                    ├── MovieDetailsScreen
│ 
│  ├── FavoritesStack├── FavoritesScreen
                     ├── MovieDetailsScreen

Here is my current typing for my child component props:

type Props = {
  navigation: SearchNavigationProp | FavoritesNavigationProp;
  ...
};

export default function DisplayMoviesList(props: Props) {
  ...

  function _navigateToMovieDetails(id: Id) {
    navigation.navigate('MovieDetails', { id: id });
  }

  return (
...
)
}

Both Stack possess the MovieDetails routes:

export type FavoritesStackParamList = {
  Favorites: undefined;
  MovieDetails: { id: Id };
};

export type SearchStackParamList = {
  Search: undefined;
  MovieDetails: { id: Id };
};

My problem is in my child component, the line navigation.navigate('MovieDetails', { id: id }); rise a typescript error:

This expression is not callable.
  Each member of the union type '{ <RouteName extends keyof FavoritesStackParamList | keyof RootTabParamList>(...args: undefined extends (FavoritesStackParamList & RootTabParamList)[RouteName] ? [screen: ...] | [screen: ...] : [screen: ...]): void; <RouteName extends keyof FavoritesStackParamList | keyof RootTabParamList>(options: { ...; } | { ...;...' has signatures, but none of those signatures are compatible with each other.

I'm not sure how to solve this problem, is there a better way to Union Type in this context ? Or my navigation structure is not correct ? Except the Typescript error, my app is working as expected.

Upvotes: 3

Views: 497

Answers (1)

Sithis
Sithis

Reputation: 41

I managed to quick fix this problem using User-defined Type Guards:

function isSearchNavigationProp(
  navigation: Navigation
): navigation is SearchNavigationProp {
  return (navigation as SearchNavigationProp).navigate !== undefined;
}


export default function DisplayMoviesList(props: Props) {
  ...

  function _navigateToMovieDetails(id: Id) {
    if (isSearchNavigationProp(navigation)) {
      navigation.navigate('MovieDetails', { id: id });
    } else {
      navigation.navigate('MovieDetails', { id: id });
    }
  }

However it is not really correct because boths my types possess the navigate method so the else is actually useless. But TypeScript is happy :).

I've seen similar cases with typescript when mapping union typed object.

If anyone have a better method I am really curious about this .

Upvotes: 1

Related Questions