Reputation: 41
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
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