Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save emeraldsanto/f9c604d6df6a83427dd8411e44bdf3d2 to your computer and use it in GitHub Desktop.

Select an option

Save emeraldsanto/f9c604d6df6a83427dd8411e44bdf3d2 to your computer and use it in GitHub Desktop.
Achieving type safe deep linking in React Native with react-navigation #7
/**
* This simply aggregates navigator constructors
* and allows us to support more than one type of navigator.
*/
const navigators = {
stack: createStackNavigator,
tab: createBottomTabNavigator,
} as const;
/**
* This creates a union type of `'stack' | 'tab'`.
*/
type NavigatorType = keyof typeof navigators;
/**
* This extracts the type of props expected by
* the component returned by each of the navigator
* constructors defined above in `navigators`.
*/
type NavigatorProps<T extends NavigatorType> = ComponentProps<
ReturnType<typeof navigators[T]>['Navigator']
>;
/**
* This defines a type accepting any `Navigator` or `Screen` as key
* paired with the following values:
* - screen: the component to render for this screen.
* - options?: the navigation options to use for this screen.
* - initialParams?: if the screen is present in `ScreenParameterMap`, extract the expected parameters otherwise allow nothing.
*/
type ScreenMap = Partial<
{
[key in Navigator | Screen]: {
screen: ComponentType<any>;
options?: StackNavigationOptions;
initialParams?: key extends keyof ScreenParameterMap ? ScreenParameterMap[key] : never;
};
}
>;
/**
* This is how we will configure our navigator, this type is input for `makeNavigator`.
* Since `screens` and `linking` are defined in the same object, we can ensure that the compiler
* will warn us if entries present in `screens` are missing in `linking`.
*
* We can specify a custom render function and navigator level options
* here while the screen level options are to be defined in `ScreenMap`.
*/
interface PartialNavigatorConfiguration<
TNavigatorType extends NavigatorType,
TScreens extends ScreenMap
> {
name: Stacks;
type: TNavigatorType;
screens: TScreens;
linking: {
path?: string;
initialRouteName?: Navigator | Screen;
screens: Record<keyof TScreens, string | PathConfig>;
};
options?: Omit<NavigatorProps<TNavigatorType>, 'children'>;
render?: ComponentType<Omit<PartialNavigatorConfiguration<TNavigatorType, TScreens>, 'render'>>;
}
/**
* This is what `makeNavigator` will return, it simply extends the shape of `PartialNavigatorConfiguration`
* while overriding the type of `render` to tell the compiler it will always be present, as opposed to what
* is defined in `PartialNavigatorConfiguration`.
*/
interface NavigatorConfiguration<
TNavigatorType extends NavigatorType,
TScreens extends ScreenMap
> extends Omit<PartialNavigatorConfiguration<TNavigatorType, TScreens>, 'render'> {
render: ComponentType<any>;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment