Skip to content

Commit

Permalink
feat: makes NavigationContainerRef.getCurrentRoute type safe
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasloisp committed Nov 23, 2023
1 parent 8e7ac4f commit 72eea65
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 2 deletions.
35 changes: 35 additions & 0 deletions example/__typechecks__/common.check.tsx
Expand Up @@ -10,8 +10,10 @@ import type {
} from '@react-navigation/drawer';
import type {
CompositeScreenProps,
NavigationContainerRef,
NavigationHelpers,
NavigatorScreenParams,
Route,
} from '@react-navigation/native';
import {
createStackNavigator,
Expand Down Expand Up @@ -352,3 +354,36 @@ export const ThirdScreen = ({
// @ts-expect-error
if (ScreenName === 'NoParams') navigation.navigate(ScreenName, { id: '123' });
};

/**
* Check for errors on getCurrentRoute
*/
declare const navigationRef: NavigationContainerRef<RootStackParamList>;
const route = navigationRef.getCurrentRoute()!;

switch (route.name) {
case 'PostDetails':
expectTypeOf(route.params).toMatchTypeOf<{
id: string;
section?: string;
}>();
break;
case 'Login':
expectTypeOf(route.params).toMatchTypeOf<undefined>();
break;
// Checks for nested routes
case 'Account':
expectTypeOf(route.params).toMatchTypeOf<undefined>();
break;
case 'Popular':
expectTypeOf(route.params).toMatchTypeOf<{
filter: 'day' | 'week' | 'month';
}>();
break;
}

declare const navigationRefUntyped: NavigationContainerRef<string>;

expectTypeOf(navigationRefUntyped.getCurrentRoute()).toMatchTypeOf<
Route<string> | undefined
>();
35 changes: 35 additions & 0 deletions example/__typechecks__/static.check.tsx
Expand Up @@ -2,6 +2,7 @@

import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import type {
NavigationContainerRef,
NavigationProp,
StaticParamList,
StaticScreenProps,
Expand Down Expand Up @@ -198,3 +199,37 @@ createStackNavigator({});
createStackNavigator({
screens: {},
});

/**
* Check for errors on getCurrentRoute
*/
declare const navigationRef: NavigationContainerRef<ParamList>;
const route = navigationRef.getCurrentRoute()!;

switch (route.name) {
case 'Profile':
expectTypeOf(route.params).toMatchTypeOf<{
user: string;
}>();
break;
case 'Settings':
expectTypeOf(route.params).toMatchTypeOf<undefined>();
break;
case 'Login':
expectTypeOf(route.params).toMatchTypeOf<undefined>();
break;
case 'Register':
expectTypeOf(route.params).toMatchTypeOf<{
method: 'email' | 'social';
}>();
break;
// Checks for nested routes
case 'Groups':
expectTypeOf(route.params).toMatchTypeOf<undefined>();
break;
case 'Chat':
expectTypeOf(route.params).toMatchTypeOf<{
id: number;
}>();
break;
}
2 changes: 1 addition & 1 deletion packages/core/src/StaticNavigation.tsx
Expand Up @@ -46,7 +46,7 @@ type ParamsForScreenComponent<T> = T extends {
type ParamsForScreen<T> = T extends { screen: StaticNavigation<any, any, any> }
? NavigatorScreenParams<StaticParamList<T['screen']>> | undefined
: T extends StaticNavigation<any, any, any>
? NavigatorScreenParams<StaticParamList<T>> | undefined
? NavigatorScreenParams<StaticParamList<T>>
: UnknownToUndefined<ParamsForScreenComponent<T>>;

type ParamListForScreens<Screens> = {
Expand Down
14 changes: 13 additions & 1 deletion packages/core/src/types.tsx
Expand Up @@ -695,6 +695,18 @@ export type NavigationContainerEventMap = {
};
};

type ParamListRoute<ParamList extends ParamListBase> = {
[RouteName in keyof ParamList]: ParamList[RouteName] extends NavigatorScreenParams<
infer T extends ParamListBase
>
? ParamListRoute<T>
: Route<Extract<RouteName, string>, ParamList[RouteName]>;
}[keyof ParamList];

type MaybeParamListRoute<ParamList extends {}> = ParamList extends ParamListBase
? ParamListRoute<ParamList>
: Route<string>;

export type NavigationContainerRef<ParamList extends {}> =
NavigationHelpers<ParamList> &
EventConsumer<NavigationContainerEventMap> & {
Expand All @@ -711,7 +723,7 @@ export type NavigationContainerRef<ParamList extends {}> =
/**
* Get the currently focused navigation route.
*/
getCurrentRoute(): Route<string> | undefined;
getCurrentRoute(): MaybeParamListRoute<ParamList> | undefined;
/**
* Get the currently focused route's options.
*/
Expand Down

0 comments on commit 72eea65

Please sign in to comment.