import type { DefaultRouterOptions, InitialState, NavigationAction, NavigationState, ParamListBase, PartialState, Route, } from '@react-navigation/routers'; import type * as React from 'react'; declare global { // eslint-disable-next-line @typescript-eslint/no-namespace namespace ReactNavigation { // eslint-disable-next-line @typescript-eslint/no-empty-interface interface RootParamList {} } } type Keyof = Extract; export type DefaultNavigatorOptions< ParamList extends ParamListBase, State extends NavigationState, ScreenOptions extends {}, EventMap extends EventMapBase > = DefaultRouterOptions> & { /** * Optional ID for the navigator. Can be used with `navigation.getParent(id)` to refer to a parent. */ id?: string; /** * Children React Elements to extract the route configuration from. * Only `Screen`, `Group` and `React.Fragment` are supported as children. */ children: React.ReactNode; /** * Event listeners for all the screens in the navigator. */ screenListeners?: | ScreenListeners | ((props: { route: RouteProp; navigation: any; }) => ScreenListeners); /** * Default options for all screens under this navigator. */ screenOptions?: | ScreenOptions | ((props: { route: RouteProp; navigation: any; }) => ScreenOptions); /** * Default options specified by the navigator. * It receives the custom options in the arguments if a function is specified. */ defaultScreenOptions?: | ScreenOptions | ((props: { route: RouteProp; navigation: any; options: ScreenOptions; }) => ScreenOptions); }; export type EventMapBase = Record< string, { data?: any; canPreventDefault?: boolean } >; export type EventMapCore = { focus: { data: undefined }; blur: { data: undefined }; state: { data: { state: State } }; beforeRemove: { data: { action: NavigationAction }; canPreventDefault: true }; }; export type EventArg< EventName extends string, CanPreventDefault extends boolean | undefined = false, Data = undefined > = { /** * Type of the event (e.g. `focus`, `blur`) */ readonly type: EventName; readonly target?: string; } & (CanPreventDefault extends true ? { /** * Whether `event.preventDefault()` was called on this event object. */ readonly defaultPrevented: boolean; /** * Prevent the default action which happens on this event. */ preventDefault(): void; } : {}) & (undefined extends Data ? { readonly data?: Readonly } : { readonly data: Readonly }); export type EventListenerCallback< EventMap extends EventMapBase, EventName extends keyof EventMap > = ( e: EventArg< Extract, EventMap[EventName]['canPreventDefault'], EventMap[EventName]['data'] > ) => void; export type EventConsumer = { /** * Subscribe to events from the parent navigator. * * @param type Type of the event (e.g. `focus`, `blur`) * @param callback Callback listener which is executed upon receiving the event. */ addListener>( type: EventName, callback: EventListenerCallback ): () => void; removeListener>( type: EventName, callback: EventListenerCallback ): void; }; export type EventEmitter = { /** * Emit an event to child screens. * * @param options.type Type of the event (e.g. `focus`, `blur`) * @param [options.data] Optional information regarding the event. * @param [options.target] Key of the target route which should receive the event. * If not specified, all routes receive the event. */ emit>( options: { type: EventName; target?: string; } & (EventMap[EventName]['canPreventDefault'] extends true ? { canPreventDefault: true } : {}) & (undefined extends EventMap[EventName]['data'] ? { data?: EventMap[EventName]['data'] } : { data: EventMap[EventName]['data'] }) ): EventArg< EventName, EventMap[EventName]['canPreventDefault'], EventMap[EventName]['data'] >; }; export class PrivateValueStore { /** * UGLY HACK! DO NOT USE THE TYPE!!! * * TypeScript requires a type to be used to be able to infer it. * The type should exist as its own without any operations such as union. * So we need to figure out a way to store this type in a property. * The problem with a normal property is that it shows up in intelliSense. * Adding private keyword works, but the annotation is stripped away in declaration. * Turns out if we use an empty string, it doesn't show up in intelliSense. */ protected ''?: T; } type NavigationHelpersCommon< ParamList extends ParamListBase, State extends NavigationState = NavigationState > = { /** * Dispatch an action or an update function to the router. * The update function will receive the current state, * * @param action Action object or update function. */ dispatch( action: NavigationAction | ((state: State) => NavigationAction) ): void; /** * Navigate to a route in current navigation tree. * * @param name Name of the route to navigate to. * @param [params] Params object for the route. */ navigate( ...args: undefined extends ParamList[RouteName] ? [screen: RouteName] | [screen: RouteName, params: ParamList[RouteName]] : [screen: RouteName, params: ParamList[RouteName]] ): void; /** * Navigate to a route in current navigation tree. * * @param route Object with `key` or `name` for the route to navigate to, and a `params` object. */ navigate( options: | { key: string; params?: ParamList[RouteName]; merge?: boolean } | { name: RouteName; key?: string; params: ParamList[RouteName]; merge?: boolean; } ): void; /** * Reset the navigation state to the provided state. * * @param state Navigation state object. */ reset(state: PartialState | State): void; /** * Go back to the previous route in history. */ goBack(): void; /** * Check if the screen is focused. The method returns `true` if focused, `false` otherwise. * Note that this method doesn't re-render screen when the focus changes. So don't use it in `render`. * To get notified of focus changes, use `addListener('focus', cb)` and `addListener('blur', cb)`. * To conditionally render content based on focus state, use the `useIsFocused` hook. */ isFocused(): boolean; /** * Check if dispatching back action will be handled by navigation. * Note that this method doesn't re-render screen when the result changes. So don't use it in `render`. */ canGoBack(): boolean; /** * Returns the name of the navigator specified in the `name` prop. * If no name is specified, returns `undefined`. */ getId(): string | undefined; /** * Returns the navigation helpers from a parent navigator based on the ID. * If an ID is provided, the navigation helper from the parent navigator with matching ID (including current) will be returned. * If no ID is provided, the navigation helper from the immediate parent navigator will be returned. * * @param id Optional ID of a parent navigator. */ getParent | undefined>(id?: string): T; /** * Returns the navigator's state. * Note that this method doesn't re-render screen when the result changes. So don't use it in `render`. */ getState(): State; } & PrivateValueStore<[ParamList, unknown, unknown]>; export type NavigationHelpers< ParamList extends ParamListBase, EventMap extends EventMapBase = {} > = NavigationHelpersCommon & EventEmitter & { /** * Update the param object for the route. * The new params will be shallow merged with the old one. * * @param params Params object for the current route. */ setParams( params: Partial ): void; }; export type NavigationContainerProps = { /** * Initial navigation state for the child navigators. */ initialState?: InitialState; /** * Callback which is called with the latest navigation state when it changes. */ onStateChange?: (state: NavigationState | undefined) => void; /** * Callback which is called when an action is not handled. */ onUnhandledAction?: (action: NavigationAction) => void; /** * Whether this navigation container should be independent of parent containers. * If this is not set to `true`, this container cannot be nested inside another container. * Setting it to `true` disconnects any children navigators from parent container. */ independent?: boolean; /** * Children elements to render. */ children: React.ReactNode; }; export type NavigationProp< ParamList extends {}, RouteName extends keyof ParamList = Keyof, NavigatorID extends string | undefined = undefined, State extends NavigationState = NavigationState, ScreenOptions extends {} = {}, EventMap extends EventMapBase = {} > = Omit, 'getParent'> & { /** * Returns the navigation prop from a parent navigator based on the ID. * If an ID is provided, the navigation prop from the parent navigator with matching ID (including current) will be returned. * If no ID is provided, the navigation prop from the immediate parent navigator will be returned. * * @param id Optional ID of a parent navigator. */ getParent | undefined>(id?: NavigatorID): T; /** * Update the param object for the route. * The new params will be shallow merged with the old one. * * @param params Params object for the current route. */ setParams(params: Partial): void; /** * Update the options for the route. * The options object will be shallow merged with default options object. * * @param options Options object for the route. */ setOptions(options: Partial): void; } & EventConsumer> & PrivateValueStore<[ParamList, RouteName, EventMap]>; export type RouteProp< ParamList extends ParamListBase, RouteName extends keyof ParamList = Keyof > = Route, ParamList[RouteName]>; export type CompositeNavigationProp< A extends NavigationProp, B extends NavigationHelpersCommon > = Omit> & NavigationProp< /** * Param list from both navigation objects needs to be combined * For example, we should be able to navigate to screens in both A and B */ (A extends NavigationHelpersCommon ? T : never) & (B extends NavigationHelpersCommon ? U : never), /** * The route name should refer to the route name specified in the first type * Ideally it should work for any of them, but it's not possible to infer that way */ A extends NavigationProp ? R : string, /** * ID from both navigation objects needs to be combined for `getParent` */ | (A extends NavigationProp ? I : never) | (B extends NavigationProp ? J : never), /** * The type of state should refer to the state specified in the first type */ A extends NavigationProp ? S : NavigationState, /** * Screen options from both navigation objects needs to be combined * This allows typechecking `setOptions` */ (A extends NavigationProp ? O : {}) & (B extends NavigationProp ? P : {}), /** * Event consumer config should refer to the config specified in the first type * This allows typechecking `addListener`/`removeListener` */ A extends NavigationProp ? E : {} >; export type CompositeScreenProps< A extends { navigation: NavigationProp< ParamListBase, string, string | undefined, any, any, any >; route: RouteProp; }, B extends { navigation: NavigationHelpersCommon; } > = { navigation: CompositeNavigationProp; route: A['route']; }; export type Descriptor< ScreenOptions extends {}, Navigation extends NavigationProp, Route extends RouteProp > = { /** * Render the component associated with this route. */ render(): JSX.Element; /** * Options for the route. */ options: ScreenOptions; /** * Route object for the screen */ route: Route; /** * Navigation object for the screen */ navigation: Navigation; }; export type ScreenListeners< State extends NavigationState, EventMap extends EventMapBase > = Partial<{ [EventName in keyof (EventMap & EventMapCore)]: EventListenerCallback< EventMap, EventName >; }>; export type RouteConfigComponent< ParamList extends ParamListBase, RouteName extends keyof ParamList > = | { /** * React component to render for this screen. */ component: React.ComponentType; getComponent?: never; children?: never; } | { /** * Lazily get a React component to render for this screen. */ getComponent: () => React.ComponentType; component?: never; children?: never; } | { /** * Render callback to render content of this screen. */ children: (props: { route: RouteProp; navigation: any; }) => React.ReactNode; component?: never; getComponent?: never; }; export type RouteConfig< ParamList extends ParamListBase, RouteName extends keyof ParamList, State extends NavigationState, ScreenOptions extends {}, EventMap extends EventMapBase > = { /** * Optional key for this screen. This doesn't need to be unique. * If the key changes, existing screens with this name will be removed or reset. * Useful when we have some common screens and have conditional rendering. */ navigationKey?: string; /** * Route name of this screen. */ name: RouteName; /** * Navigator options for this screen. */ options?: | ScreenOptions | ((props: { route: RouteProp; navigation: any; }) => ScreenOptions); /** * Event listeners for this screen. */ listeners?: | ScreenListeners | ((props: { route: RouteProp; navigation: any; }) => ScreenListeners); /** * Function to return an unique ID for this screen. * Receives an object with the route params. * For a given screen name, there will always be only one screen corresponding to an ID. * If `undefined` is returned, it acts same as no `getId` being specified. */ getId?: ({ params }: { params: ParamList[RouteName] }) => string | undefined; /** * Initial params object for the route. */ initialParams?: Partial; } & RouteConfigComponent; export type RouteGroupConfig< ParamList extends ParamListBase, ScreenOptions extends {} > = { /** * Optional key for the screens in this group. * If the key changes, all existing screens in this group will be removed or reset. */ navigationKey?: string; /** * Navigator options for this screen. */ screenOptions?: | ScreenOptions | ((props: { route: RouteProp; navigation: any; }) => ScreenOptions); /** * Children React Elements to extract the route configuration from. * Only `Screen`, `Group` and `React.Fragment` are supported as children. */ children: React.ReactNode; }; export type NavigationContainerEventMap = { /** * Event which fires when the navigation state changes. */ state: { data: { /** * The updated state object after the state change. */ state: NavigationState | PartialState | undefined; }; }; /** * Event which fires when current options changes. */ options: { data: { options: object } }; /** * Event which fires when an action is dispatched. * Only intended for debugging purposes, don't use it for app logic. * This event will be emitted before state changes have been applied. */ __unsafe_action__: { data: { /** * The action object which was dispatched. */ action: NavigationAction; /** * Whether the action was a no-op, i.e. resulted any state changes. */ noop: boolean; /** * Stack trace of the action, this will only be available during development. */ stack: string | undefined; }; }; }; export type NavigationContainerRef = NavigationHelpers & EventConsumer & { /** * Reset the navigation state of the root navigator to the provided state. * * @param state Navigation state object. */ resetRoot(state?: PartialState | NavigationState): void; /** * Get the rehydrated navigation state of the navigation tree. */ getRootState(): NavigationState; /** * Get the currently focused navigation route. */ getCurrentRoute(): Route | undefined; /** * Get the currently focused route's options. */ getCurrentOptions(): object | undefined; /** * Whether the navigation container is ready to handle actions. */ isReady(): boolean; }; export type NavigationContainerRefWithCurrent = NavigationContainerRef & { current: NavigationContainerRef | null; }; export type TypedNavigator< ParamList extends ParamListBase, State extends NavigationState, ScreenOptions extends {}, EventMap extends EventMapBase, Navigator extends React.ComponentType > = { /** * Navigator component which manages the child screens. */ Navigator: React.ComponentType< Omit< React.ComponentProps, keyof DefaultNavigatorOptions > & DefaultNavigatorOptions >; /** * Component used for grouping multiple route configuration. */ Group: React.ComponentType>; /** * Component used for specifying route configuration. */ Screen: ( _: RouteConfig ) => null; }; export type NavigatorScreenParams< ParamList, State extends NavigationState = NavigationState > = | { screen?: never; params?: never; initial?: never; path?: string; state: PartialState | State | undefined; } | { [RouteName in keyof ParamList]: undefined extends ParamList[RouteName] ? { screen: RouteName; params?: ParamList[RouteName]; initial?: boolean; path?: string; state?: never; } : { screen: RouteName; params: ParamList[RouteName]; initial?: boolean; path?: string; state?: never; }; }[keyof ParamList]; export type PathConfig = { path?: string; exact?: boolean; parse?: Record any>; stringify?: Record string>; screens?: PathConfigMap; initialRouteName?: keyof ParamList; }; export type PathConfigMap = { [RouteName in keyof ParamList]?: NonNullable< ParamList[RouteName] > extends NavigatorScreenParams ? string | PathConfig : string | Omit, 'screens' | 'initialRouteName'>; };