import type { DefaultRouterOptions, InitialState, NavigationAction, NavigationState, ParamListBase, PartialState, Route, Router, } 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 {} // eslint-disable-next-line @typescript-eslint/no-empty-interface interface Theme {} } } type Keyof = Extract; export type DefaultNavigatorOptions< ParamList extends ParamListBase, NavigatorID extends string | undefined, State extends NavigationState, ScreenOptions extends {}, EventMap extends EventMapBase, Navigation, > = DefaultRouterOptions> & { /** * Children React Elements to extract the route configuration from. * Only `Screen`, `Group` and `React.Fragment` are supported as children. */ children: React.ReactNode; /** * Layout for the navigator. * Useful for wrapping with a component with access to navigator's state and options. */ layout?: (props: { state: State; navigation: NavigationHelpers; descriptors: Record< string, Descriptor< ScreenOptions, NavigationProp< ParamList, keyof ParamList, string | undefined, State, ScreenOptions, EventMap >, RouteProp > >; children: React.ReactNode; }) => React.ReactElement; /** * Event listeners for all the screens in the navigator. */ screenListeners?: | ScreenListeners | ((props: { route: RouteProp; navigation: Navigation; }) => ScreenListeners); /** * Default options for all screens under this navigator. */ screenOptions?: | ScreenOptions | ((props: { route: RouteProp; navigation: Navigation; theme: ReactNavigation.Theme; }) => ScreenOptions); /** * Layout for all screens under this navigator. */ screenLayout?: ( props: ScreenLayoutArgs< ParamList, keyof ParamList, ScreenOptions, Navigation > ) => React.ReactElement; /** * A function returning overrides for the underlying router used by the navigator. * The overrides will be shallow merged onto the original router. * It receives the original router as an argument to the function. * * This must be a pure function and cannot reference outside dynamic variables. */ UNSTABLE_router?: ( original: Router ) => Partial>; } & (NavigatorID extends string ? { /** * Optional ID for the navigator. Can be used with `navigation.getParent(id)` to refer to a parent. */ id: NavigatorID; } : { id?: undefined; }); 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, 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, EventCanPreventDefault extends | boolean | undefined = EventMap[EventName]['canPreventDefault'], > = ( e: EventArg< EventName, undefined extends EventCanPreventDefault ? false : EventCanPreventDefault, 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: Readonly) => NavigationAction) ): void; /** * Navigate to a screen in the current or parent navigator. * If we're already on the screen, update the params instead. * * @param screen Name of the route to navigate to. * @param [params] Params object for the route. * @param [options.merge] Whether to merge the params onto the route. Defaults to `false`. * @param [options.pop] Whether to pop routes in a stack to go back to the matching route. Defaults to `false`. */ navigate( ...args: // This condition allows us to iterate over a union type // This is to avoid getting a union of all the params from `ParamList[RouteName]`, // which will get our types all mixed up if a union RouteName is passed in. RouteName extends unknown ? // This condition checks if the params are optional, // which means it's either undefined or a union with undefined undefined extends ParamList[RouteName] ? [ screen: RouteName, params?: ParamList[RouteName], options?: { merge?: boolean; pop?: boolean }, ] : [ screen: RouteName, params: ParamList[RouteName], options?: { merge?: boolean; pop?: boolean }, ] : never ): void; /** * Navigate to a route in current navigation tree. * * @param options.name Name of the route to navigate to. * @param [options.params] Params object for the route. * @param [options.path] Path to associate the route with (e.g. for deep links). * @param [options.merge] Whether to merge the params onto the route. Defaults to `false`. * @param [options.pop] Whether to pop routes in a stack to go back to the matching route. Defaults to `false`. */ navigate( options: RouteName extends unknown ? { name: RouteName; params: ParamList[RouteName]; path?: string; merge?: boolean; pop?: boolean; } : never ): void; /** * Navigate to a route in current navigation tree. * * @deprecated Use `navigate` instead. * * @param screen Name of the route to navigate to. * @param [params] Params object for the route. */ navigateDeprecated( ...args: RouteName extends unknown ? undefined extends ParamList[RouteName] ? [screen: RouteName, params?: ParamList[RouteName]] : [screen: RouteName, params: ParamList[RouteName]] : never ): void; /** * Navigate to a route in current navigation tree. * * @deprecated Use `navigate` instead. * * @param options Object with `name` for the route to navigate to, and a `params` object. */ navigateDeprecated( options: RouteName extends unknown ? { name: RouteName; params: ParamList[RouteName]; merge?: boolean; } : never ): void; /** * Preloads the route in current navigation tree. * * @param screen Name of the route to preload. * @param [params] Params object for the route. */ preload( ...args: RouteName extends unknown ? undefined extends ParamList[RouteName] ? [screen: RouteName, params?: ParamList[RouteName]] : [screen: RouteName, params: ParamList[RouteName]] : never ): 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]>; type NavigationHelpersRoute< ParamList extends {}, RouteName extends keyof ParamList = Keyof, > = { /** * Update the param object for the route. * The new params will be shallow merged with the old one. * * @param params Partial params object for the current route. */ setParams( params: ParamList[RouteName] extends undefined ? undefined : Partial ): void; /** * Replace the param object for the route * * @param params Params object for the current route. */ replaceParams( params: ParamList[RouteName] extends undefined ? undefined : ParamList[RouteName] ): void; }; export type NavigationHelpers< ParamList extends ParamListBase, EventMap extends EventMapBase = {}, > = NavigationHelpersCommon & EventEmitter & NavigationHelpersRoute; 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: Readonly | undefined) => void; /** * Callback which is called after the navigation tree mounts. */ onReady?: () => void; /** * Callback which is called when an action is not handled. */ onUnhandledAction?: (action: Readonly) => void; /** * Whether child navigator should handle a navigation action. * The child navigator needs to be mounted before it can handle the action. * Defaults to `false`. * * This will be removed in the next major release. * * @deprecated Use nested navigation API instead */ navigationInChildEnabled?: boolean; /** * Theme object for the UI elements. */ theme?: ReactNavigation.Theme; /** * 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 options for the route. * The options object will be shallow merged with default options object. * * @param update Options object or a callback which takes the options from navigator config and returns a new options object. */ setOptions(options: Partial): void; } & NavigationHelpersRoute & 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 should refer to the options specified in the first type */ A extends NavigationProp ? O : {}, /** * 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 ScreenLayoutArgs< ParamList extends ParamListBase, RouteName extends keyof ParamList, ScreenOptions extends {}, Navigation, > = { route: RouteProp; options: ScreenOptions; navigation: Navigation; theme: ReactNavigation.Theme; children: React.ReactElement; }; export type Descriptor< ScreenOptions extends {}, Navigation extends NavigationProp, Route extends RouteProp, > = { /** * Render the component associated with this route. */ render(): React.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 & EventMapCore, EventName >; }>; type ScreenComponentType< ParamList extends ParamListBase, RouteName extends keyof ParamList, > = | React.ComponentType<{ route: RouteProp; navigation: any; }> | React.ComponentType<{}>; export type RouteConfigComponent< ParamList extends ParamListBase, RouteName extends keyof ParamList, > = | { /** * React component to render for this screen. */ component: ScreenComponentType; getComponent?: never; children?: never; } | { /** * Lazily get a React component to render for this screen. */ getComponent: () => ScreenComponentType; 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 RouteConfigProps< ParamList extends ParamListBase, RouteName extends keyof ParamList, State extends NavigationState, ScreenOptions extends {}, EventMap extends EventMapBase, Navigation, > = { /** * 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: Navigation; theme: ReactNavigation.Theme; }) => ScreenOptions); /** * Event listeners for this screen. */ listeners?: | ScreenListeners | ((props: { route: RouteProp; navigation: Navigation; }) => ScreenListeners); /** * Layout for this screen. * Useful for wrapping the screen with custom containers. * e.g. for styling, error boundaries, suspense, etc. */ layout?: ( props: ScreenLayoutArgs ) => React.ReactElement; /** * 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: Readonly; }) => string | undefined; /** * Initial params object for the route. */ initialParams?: Partial; }; export type RouteConfig< ParamList extends ParamListBase, RouteName extends keyof ParamList, State extends NavigationState, ScreenOptions extends {}, EventMap extends EventMapBase, Navigation, > = RouteConfigProps< ParamList, RouteName, State, ScreenOptions, EventMap, Navigation > & RouteConfigComponent; export type RouteGroupConfig< ParamList extends ParamListBase, ScreenOptions extends {}, Navigation, > = { /** * 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: Navigation; theme: ReactNavigation.Theme; }) => ScreenOptions); /** * Layout for the screens inside the group. * This will override the `screenLayout` of parent group or navigator. */ screenLayout?: | (( props: ScreenLayoutArgs< ParamList, keyof ParamList, ScreenOptions, Navigation > ) => React.ReactElement) | { // FIXME: TypeScript doesn't seem to infer `navigation` correctly without this }; /** * 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 that fires when the navigation container is ready to be used. */ ready: { data: undefined; }; /** * Event that fires when the navigation state changes. */ state: { data: { /** * The updated state object after the state change. */ state: NavigationState | PartialState | undefined; }; }; /** * Event that fires when current options changes. */ options: { data: { options: object } }; /** * Event that 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 that 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; }; }; }; type NotUndefined = T extends undefined ? never : T; export type ParamListRoute = { [RouteName in keyof ParamList]: NavigatorScreenParams<{}> extends ParamList[RouteName] ? NotUndefined extends NavigatorScreenParams ? ParamListRoute : Route, ParamList[RouteName]> : Route, ParamList[RouteName]>; }[keyof ParamList]; type MaybeParamListRoute = ParamList extends ParamListBase ? ParamListRoute : Route; 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(): MaybeParamListRoute | undefined; /** * Get the currently focused route's options. */ getCurrentOptions(): object | undefined; /** * Whether the navigation container is ready to handle actions. */ isReady(): boolean; /** * Stub function for setOptions on navigation object for use with useNavigation. */ setOptions(): never; /** * Stub function for getParent on navigation object for use with useNavigation. */ getParent(): undefined; }; export type NavigationContainerRefWithCurrent = NavigationContainerRef & { current: NavigationContainerRef | null; }; export type NavigationListBase = { [RouteName in keyof ParamList]: unknown; }; export type TypeBag< ParamList extends ParamListBase, NavigatorID extends string | undefined, State extends NavigationState, ScreenOptions extends {}, EventMap extends EventMapBase, NavigationList extends NavigationListBase, Navigator extends React.ComponentType, > = { ParamList: ParamList; NavigatorID: NavigatorID; State: State; ScreenOptions: ScreenOptions; EventMap: EventMap; NavigationList: NavigationList; Navigator: Navigator; }; export type NavigatorTypeBagBase = { ParamList: {}; NavigatorID: string | undefined; State: NavigationState; ScreenOptions: {}; EventMap: {}; NavigationList: NavigationListBase; Navigator: React.ComponentType; }; export type NavigatorTypeBag< ParamList extends ParamListBase, NavigatorID extends string | undefined, State extends NavigationState, ScreenOptions extends {}, EventMap extends EventMapBase, NavigationList extends NavigationListBase, Navigator extends React.ComponentType, > = { ParamList: ParamList; NavigatorID: NavigatorID; State: State; ScreenOptions: ScreenOptions; EventMap: EventMap; NavigationList: NavigationList; Navigator: Navigator; }; export type TypedNavigator< Bag extends NavigatorTypeBagBase, Config = unknown, > = TypedNavigatorInternal< Bag['ParamList'], Bag['NavigatorID'], Bag['State'], Bag['ScreenOptions'], Bag['EventMap'], Bag['NavigationList'], Bag['Navigator'] > & (undefined extends Config ? {} : { config: Config }); type TypedNavigatorInternal< ParamList extends ParamListBase, NavigatorID extends string | undefined, State extends NavigationState, ScreenOptions extends {}, EventMap extends EventMapBase, NavigationList extends NavigationListBase, Navigator extends React.ComponentType, > = { /** * Navigator component which manages the child screens. */ Navigator: React.ComponentType< Omit< React.ComponentProps, keyof DefaultNavigatorOptions > & DefaultNavigatorOptions< ParamList, NavigatorID, State, ScreenOptions, EventMap, NavigationList[keyof ParamList] > >; /** * Component used for grouping multiple route configuration. */ Group: React.ComponentType< RouteGroupConfig >; /** * Component used for specifying route configuration. */ Screen: ( _: RouteConfig< ParamList, RouteName, State, ScreenOptions, EventMap, NavigationList[RouteName] > ) => null; }; export type NavigatorScreenParams = | { screen?: never; params?: never; merge?: never; initial?: never; pop?: never; path?: string; state: PartialState | NavigationState | undefined; } | { [RouteName in keyof ParamList]: undefined extends ParamList[RouteName] ? { screen: RouteName; params?: ParamList[RouteName]; merge?: boolean; initial?: boolean; path?: string; pop?: boolean; state?: never; } : { screen: RouteName; params: ParamList[RouteName]; merge?: boolean; initial?: boolean; path?: string; pop?: boolean; state?: never; }; }[keyof ParamList]; type PathConfigAlias = { /** * Path string to match against. * e.g. `/users/:id` will match `/users/1` and extract `id` param as `1`. */ path: string; /** * Whether the path should be consider parent paths or use the exact path. * By default, paths are relating to the path config on the parent screen. * If `exact` is set to `true`, the parent path configuration is not used. */ exact?: boolean; /** * An object mapping the param name to a function which parses the param value. * * @example * ```js * parse: { * id: Number, * date: (value) => new Date(value) * } * ``` */ parse?: Record any>; }; export type PathConfig = Partial & { /** * An object mapping the param name to a function which converts the param value to a string. * By default, all params are converted to strings using `String(value)`. * * @example * ```js * stringify: { * date: (value) => value.toISOString() * } * ``` */ stringify?: Record string>; /** * Additional path alias that will be matched to the same screen. */ alias?: (string | PathConfigAlias)[]; /** * Path configuration for child screens. */ screens?: PathConfigMap; /** * Name of the initial route to use for the navigator when the path matches. */ initialRouteName?: keyof ParamList; }; export type PathConfigMap = { [RouteName in keyof ParamList]?: NonNullable< ParamList[RouteName] > extends NavigatorScreenParams ? string | PathConfig : string | Omit, 'screens' | 'initialRouteName'>; };