UNPKG

21.7 kBTypeScriptView Raw
1import type {
2 DefaultRouterOptions,
3 InitialState,
4 NavigationAction,
5 NavigationState,
6 ParamListBase,
7 PartialState,
8 Route,
9} from '@react-navigation/routers';
10import type * as React from 'react';
11
12declare global {
13 // eslint-disable-next-line @typescript-eslint/no-namespace
14 namespace ReactNavigation {
15 // eslint-disable-next-line @typescript-eslint/no-empty-interface
16 interface RootParamList {}
17 }
18}
19
20type Keyof<T extends {}> = Extract<keyof T, string>;
21
22export type DefaultNavigatorOptions<
23 ParamList extends ParamListBase,
24 State extends NavigationState,
25 ScreenOptions extends {},
26 EventMap extends EventMapBase
27> = DefaultRouterOptions<Keyof<ParamList>> & {
28 /**
29 * Optional ID for the navigator. Can be used with `navigation.getParent(id)` to refer to a parent.
30 */
31 id?: string;
32 /**
33 * Children React Elements to extract the route configuration from.
34 * Only `Screen`, `Group` and `React.Fragment` are supported as children.
35 */
36 children: React.ReactNode;
37 /**
38 * Event listeners for all the screens in the navigator.
39 */
40 screenListeners?:
41 | ScreenListeners<State, EventMap>
42 | ((props: {
43 route: RouteProp<ParamList>;
44 navigation: any;
45 }) => ScreenListeners<State, EventMap>);
46 /**
47 * Default options for all screens under this navigator.
48 */
49 screenOptions?:
50 | ScreenOptions
51 | ((props: {
52 route: RouteProp<ParamList>;
53 navigation: any;
54 }) => ScreenOptions);
55 /**
56 * Default options specified by the navigator.
57 * It receives the custom options in the arguments if a function is specified.
58 */
59 defaultScreenOptions?:
60 | ScreenOptions
61 | ((props: {
62 route: RouteProp<ParamList>;
63 navigation: any;
64 options: ScreenOptions;
65 }) => ScreenOptions);
66};
67
68export type EventMapBase = Record<
69 string,
70 { data?: any; canPreventDefault?: boolean }
71>;
72
73export type EventMapCore<State extends NavigationState> = {
74 focus: { data: undefined };
75 blur: { data: undefined };
76 state: { data: { state: State } };
77 beforeRemove: { data: { action: NavigationAction }; canPreventDefault: true };
78};
79
80export type EventArg<
81 EventName extends string,
82 CanPreventDefault extends boolean | undefined = false,
83 Data = undefined
84> = {
85 /**
86 * Type of the event (e.g. `focus`, `blur`)
87 */
88 readonly type: EventName;
89 readonly target?: string;
90} & (CanPreventDefault extends true
91 ? {
92 /**
93 * Whether `event.preventDefault()` was called on this event object.
94 */
95 readonly defaultPrevented: boolean;
96 /**
97 * Prevent the default action which happens on this event.
98 */
99 preventDefault(): void;
100 }
101 : {}) &
102 (undefined extends Data
103 ? { readonly data?: Readonly<Data> }
104 : { readonly data: Readonly<Data> });
105
106export type EventListenerCallback<
107 EventMap extends EventMapBase,
108 EventName extends keyof EventMap
109> = (
110 e: EventArg<
111 Extract<EventName, string>,
112 EventMap[EventName]['canPreventDefault'],
113 EventMap[EventName]['data']
114 >
115) => void;
116
117export type EventConsumer<EventMap extends EventMapBase> = {
118 /**
119 * Subscribe to events from the parent navigator.
120 *
121 * @param type Type of the event (e.g. `focus`, `blur`)
122 * @param callback Callback listener which is executed upon receiving the event.
123 */
124 addListener<EventName extends Keyof<EventMap>>(
125 type: EventName,
126 callback: EventListenerCallback<EventMap, EventName>
127 ): () => void;
128 removeListener<EventName extends Keyof<EventMap>>(
129 type: EventName,
130 callback: EventListenerCallback<EventMap, EventName>
131 ): void;
132};
133
134export type EventEmitter<EventMap extends EventMapBase> = {
135 /**
136 * Emit an event to child screens.
137 *
138 * @param options.type Type of the event (e.g. `focus`, `blur`)
139 * @param [options.data] Optional information regarding the event.
140 * @param [options.target] Key of the target route which should receive the event.
141 * If not specified, all routes receive the event.
142 */
143 emit<EventName extends Keyof<EventMap>>(
144 options: {
145 type: EventName;
146 target?: string;
147 } & (EventMap[EventName]['canPreventDefault'] extends true
148 ? { canPreventDefault: true }
149 : {}) &
150 (undefined extends EventMap[EventName]['data']
151 ? { data?: EventMap[EventName]['data'] }
152 : { data: EventMap[EventName]['data'] })
153 ): EventArg<
154 EventName,
155 EventMap[EventName]['canPreventDefault'],
156 EventMap[EventName]['data']
157 >;
158};
159
160export class PrivateValueStore<T extends [any, any, any]> {
161 /**
162 * UGLY HACK! DO NOT USE THE TYPE!!!
163 *
164 * TypeScript requires a type to be used to be able to infer it.
165 * The type should exist as its own without any operations such as union.
166 * So we need to figure out a way to store this type in a property.
167 * The problem with a normal property is that it shows up in intelliSense.
168 * Adding private keyword works, but the annotation is stripped away in declaration.
169 * Turns out if we use an empty string, it doesn't show up in intelliSense.
170 */
171 protected ''?: T;
172}
173
174type NavigationHelpersCommon<
175 ParamList extends ParamListBase,
176 State extends NavigationState = NavigationState
177> = {
178 /**
179 * Dispatch an action or an update function to the router.
180 * The update function will receive the current state,
181 *
182 * @param action Action object or update function.
183 */
184 dispatch(
185 action: NavigationAction | ((state: State) => NavigationAction)
186 ): void;
187
188 /**
189 * Navigate to a route in current navigation tree.
190 *
191 * @param name Name of the route to navigate to.
192 * @param [params] Params object for the route.
193 */
194 navigate<RouteName extends keyof ParamList>(
195 ...args: // this first condition allows us to iterate over a union type
196 // This is to avoid getting a union of all the params from `ParamList[RouteName]`,
197 // which will get our types all mixed up if a union RouteName is passed in.
198 RouteName extends unknown
199 ? // This condition checks if the params are optional,
200 // which means it's either undefined or a union with undefined
201 undefined extends ParamList[RouteName]
202 ?
203 | [screen: RouteName] // if the params are optional, we don't have to provide it
204 | [screen: RouteName, params: ParamList[RouteName]]
205 : [screen: RouteName, params: ParamList[RouteName]]
206 : never
207 ): void;
208
209 /**
210 * Navigate to a route in current navigation tree.
211 *
212 * @param route Object with `key` or `name` for the route to navigate to, and a `params` object.
213 */
214 navigate<RouteName extends keyof ParamList>(
215 options: RouteName extends unknown
216 ?
217 | { key: string; params?: ParamList[RouteName]; merge?: boolean }
218 | {
219 name: RouteName;
220 key?: string;
221 params: ParamList[RouteName];
222 merge?: boolean;
223 }
224 : never
225 ): void;
226
227 /**
228 * Reset the navigation state to the provided state.
229 *
230 * @param state Navigation state object.
231 */
232 reset(state: PartialState<State> | State): void;
233
234 /**
235 * Go back to the previous route in history.
236 */
237 goBack(): void;
238
239 /**
240 * Check if the screen is focused. The method returns `true` if focused, `false` otherwise.
241 * Note that this method doesn't re-render screen when the focus changes. So don't use it in `render`.
242 * To get notified of focus changes, use `addListener('focus', cb)` and `addListener('blur', cb)`.
243 * To conditionally render content based on focus state, use the `useIsFocused` hook.
244 */
245 isFocused(): boolean;
246
247 /**
248 * Check if dispatching back action will be handled by navigation.
249 * Note that this method doesn't re-render screen when the result changes. So don't use it in `render`.
250 */
251 canGoBack(): boolean;
252
253 /**
254 * Returns the name of the navigator specified in the `name` prop.
255 * If no name is specified, returns `undefined`.
256 */
257 getId(): string | undefined;
258
259 /**
260 * Returns the navigation helpers from a parent navigator based on the ID.
261 * If an ID is provided, the navigation helper from the parent navigator with matching ID (including current) will be returned.
262 * If no ID is provided, the navigation helper from the immediate parent navigator will be returned.
263 *
264 * @param id Optional ID of a parent navigator.
265 */
266 getParent<T = NavigationHelpers<ParamListBase> | undefined>(id?: string): T;
267
268 /**
269 * Returns the navigator's state.
270 * Note that this method doesn't re-render screen when the result changes. So don't use it in `render`.
271 */
272 getState(): State;
273} & PrivateValueStore<[ParamList, unknown, unknown]>;
274
275export type NavigationHelpers<
276 ParamList extends ParamListBase,
277 EventMap extends EventMapBase = {}
278> = NavigationHelpersCommon<ParamList> &
279 EventEmitter<EventMap> & {
280 /**
281 * Update the param object for the route.
282 * The new params will be shallow merged with the old one.
283 *
284 * @param params Params object for the current route.
285 */
286 setParams<RouteName extends keyof ParamList>(
287 params: Partial<ParamList[RouteName]>
288 ): void;
289 };
290
291export type NavigationContainerProps = {
292 /**
293 * Initial navigation state for the child navigators.
294 */
295 initialState?: InitialState;
296 /**
297 * Callback which is called with the latest navigation state when it changes.
298 */
299 onStateChange?: (state: NavigationState | undefined) => void;
300 /**
301 * Callback which is called when an action is not handled.
302 */
303 onUnhandledAction?: (action: NavigationAction) => void;
304 /**
305 * Whether this navigation container should be independent of parent containers.
306 * If this is not set to `true`, this container cannot be nested inside another container.
307 * Setting it to `true` disconnects any children navigators from parent container.
308 */
309 independent?: boolean;
310 /**
311 * Children elements to render.
312 */
313 children: React.ReactNode;
314};
315
316export type NavigationProp<
317 ParamList extends {},
318 RouteName extends keyof ParamList = Keyof<ParamList>,
319 NavigatorID extends string | undefined = undefined,
320 State extends NavigationState = NavigationState<ParamList>,
321 ScreenOptions extends {} = {},
322 EventMap extends EventMapBase = {}
323> = Omit<NavigationHelpersCommon<ParamList, State>, 'getParent'> & {
324 /**
325 * Returns the navigation prop from a parent navigator based on the ID.
326 * If an ID is provided, the navigation prop from the parent navigator with matching ID (including current) will be returned.
327 * If no ID is provided, the navigation prop from the immediate parent navigator will be returned.
328 *
329 * @param id Optional ID of a parent navigator.
330 */
331 getParent<T = NavigationProp<ParamListBase> | undefined>(id?: NavigatorID): T;
332
333 /**
334 * Update the param object for the route.
335 * The new params will be shallow merged with the old one.
336 *
337 * @param params Params object for the current route.
338 */
339 setParams(
340 params: ParamList[RouteName] extends undefined
341 ? undefined
342 : Partial<ParamList[RouteName]>
343 ): void;
344
345 /**
346 * Update the options for the route.
347 * The options object will be shallow merged with default options object.
348 *
349 * @param options Options object for the route.
350 */
351 setOptions(options: Partial<ScreenOptions>): void;
352} & EventConsumer<EventMap & EventMapCore<State>> &
353 PrivateValueStore<[ParamList, RouteName, EventMap]>;
354
355export type RouteProp<
356 ParamList extends ParamListBase,
357 RouteName extends keyof ParamList = Keyof<ParamList>
358> = Route<Extract<RouteName, string>, ParamList[RouteName]>;
359
360export type CompositeNavigationProp<
361 A extends NavigationProp<ParamListBase, string, any, any, any>,
362 B extends NavigationHelpersCommon<ParamListBase, any>
363> = Omit<A & B, keyof NavigationProp<any>> &
364 NavigationProp<
365 /**
366 * Param list from both navigation objects needs to be combined
367 * For example, we should be able to navigate to screens in both A and B
368 */
369 (A extends NavigationHelpersCommon<infer T> ? T : never) &
370 (B extends NavigationHelpersCommon<infer U> ? U : never),
371 /**
372 * The route name should refer to the route name specified in the first type
373 * Ideally it should work for any of them, but it's not possible to infer that way
374 */
375 A extends NavigationProp<any, infer R> ? R : string,
376 /**
377 * ID from both navigation objects needs to be combined for `getParent`
378 */
379 | (A extends NavigationProp<any, any, infer I> ? I : never)
380 | (B extends NavigationProp<any, any, infer J> ? J : never),
381 /**
382 * The type of state should refer to the state specified in the first type
383 */
384 A extends NavigationProp<any, any, any, infer S> ? S : NavigationState,
385 /**
386 * Screen options from both navigation objects needs to be combined
387 * This allows typechecking `setOptions`
388 */
389 (A extends NavigationProp<any, any, any, any, infer O> ? O : {}) &
390 (B extends NavigationProp<any, any, any, any, infer P> ? P : {}),
391 /**
392 * Event consumer config should refer to the config specified in the first type
393 * This allows typechecking `addListener`/`removeListener`
394 */
395 A extends NavigationProp<any, any, any, any, any, infer E> ? E : {}
396 >;
397
398export type CompositeScreenProps<
399 A extends {
400 navigation: NavigationProp<
401 ParamListBase,
402 string,
403 string | undefined,
404 any,
405 any,
406 any
407 >;
408 route: RouteProp<ParamListBase>;
409 },
410 B extends {
411 navigation: NavigationHelpersCommon<any, any>;
412 }
413> = {
414 navigation: CompositeNavigationProp<A['navigation'], B['navigation']>;
415 route: A['route'];
416};
417
418export type Descriptor<
419 ScreenOptions extends {},
420 Navigation extends NavigationProp<any, any, any, any, any, any>,
421 Route extends RouteProp<any, any>
422> = {
423 /**
424 * Render the component associated with this route.
425 */
426 render(): JSX.Element;
427
428 /**
429 * Options for the route.
430 */
431 options: ScreenOptions;
432
433 /**
434 * Route object for the screen
435 */
436 route: Route;
437
438 /**
439 * Navigation object for the screen
440 */
441 navigation: Navigation;
442};
443
444export type ScreenListeners<
445 State extends NavigationState,
446 EventMap extends EventMapBase
447> = Partial<{
448 [EventName in keyof (EventMap & EventMapCore<State>)]: EventListenerCallback<
449 EventMap,
450 EventName
451 >;
452}>;
453
454type ScreenComponentType<
455 ParamList extends ParamListBase,
456 RouteName extends keyof ParamList
457> =
458 | React.ComponentType<{
459 route: RouteProp<ParamList, RouteName>;
460 navigation: any;
461 }>
462 | React.ComponentType<{}>;
463
464export type RouteConfigComponent<
465 ParamList extends ParamListBase,
466 RouteName extends keyof ParamList
467> =
468 | {
469 /**
470 * React component to render for this screen.
471 */
472 component: ScreenComponentType<ParamList, RouteName>;
473 getComponent?: never;
474 children?: never;
475 }
476 | {
477 /**
478 * Lazily get a React component to render for this screen.
479 */
480 getComponent: () => ScreenComponentType<ParamList, RouteName>;
481 component?: never;
482 children?: never;
483 }
484 | {
485 /**
486 * Render callback to render content of this screen.
487 */
488 children: (props: {
489 route: RouteProp<ParamList, RouteName>;
490 navigation: any;
491 }) => React.ReactNode;
492 component?: never;
493 getComponent?: never;
494 };
495
496export type RouteConfig<
497 ParamList extends ParamListBase,
498 RouteName extends keyof ParamList,
499 State extends NavigationState,
500 ScreenOptions extends {},
501 EventMap extends EventMapBase
502> = {
503 /**
504 * Optional key for this screen. This doesn't need to be unique.
505 * If the key changes, existing screens with this name will be removed or reset.
506 * Useful when we have some common screens and have conditional rendering.
507 */
508 navigationKey?: string;
509
510 /**
511 * Route name of this screen.
512 */
513 name: RouteName;
514
515 /**
516 * Navigator options for this screen.
517 */
518 options?:
519 | ScreenOptions
520 | ((props: {
521 route: RouteProp<ParamList, RouteName>;
522 navigation: any;
523 }) => ScreenOptions);
524
525 /**
526 * Event listeners for this screen.
527 */
528 listeners?:
529 | ScreenListeners<State, EventMap>
530 | ((props: {
531 route: RouteProp<ParamList, RouteName>;
532 navigation: any;
533 }) => ScreenListeners<State, EventMap>);
534
535 /**
536 * Function to return an unique ID for this screen.
537 * Receives an object with the route params.
538 * For a given screen name, there will always be only one screen corresponding to an ID.
539 * If `undefined` is returned, it acts same as no `getId` being specified.
540 */
541 getId?: ({ params }: { params: ParamList[RouteName] }) => string | undefined;
542
543 /**
544 * Initial params object for the route.
545 */
546 initialParams?: Partial<ParamList[RouteName]>;
547} & RouteConfigComponent<ParamList, RouteName>;
548
549export type RouteGroupConfig<
550 ParamList extends ParamListBase,
551 ScreenOptions extends {}
552> = {
553 /**
554 * Optional key for the screens in this group.
555 * If the key changes, all existing screens in this group will be removed or reset.
556 */
557 navigationKey?: string;
558
559 /**
560 * Navigator options for this screen.
561 */
562 screenOptions?:
563 | ScreenOptions
564 | ((props: {
565 route: RouteProp<ParamList, keyof ParamList>;
566 navigation: any;
567 }) => ScreenOptions);
568 /**
569 * Children React Elements to extract the route configuration from.
570 * Only `Screen`, `Group` and `React.Fragment` are supported as children.
571 */
572 children: React.ReactNode;
573};
574
575export type NavigationContainerEventMap = {
576 /**
577 * Event which fires when the navigation state changes.
578 */
579 state: {
580 data: {
581 /**
582 * The updated state object after the state change.
583 */
584 state: NavigationState | PartialState<NavigationState> | undefined;
585 };
586 };
587 /**
588 * Event which fires when current options changes.
589 */
590 options: { data: { options: object } };
591 /**
592 * Event which fires when an action is dispatched.
593 * Only intended for debugging purposes, don't use it for app logic.
594 * This event will be emitted before state changes have been applied.
595 */
596 __unsafe_action__: {
597 data: {
598 /**
599 * The action object which was dispatched.
600 */
601 action: NavigationAction;
602 /**
603 * Whether the action was a no-op, i.e. resulted any state changes.
604 */
605 noop: boolean;
606 /**
607 * Stack trace of the action, this will only be available during development.
608 */
609 stack: string | undefined;
610 };
611 };
612};
613
614export type NavigationContainerRef<ParamList extends {}> =
615 NavigationHelpers<ParamList> &
616 EventConsumer<NavigationContainerEventMap> & {
617 /**
618 * Reset the navigation state of the root navigator to the provided state.
619 *
620 * @param state Navigation state object.
621 */
622 resetRoot(state?: PartialState<NavigationState> | NavigationState): void;
623 /**
624 * Get the rehydrated navigation state of the navigation tree.
625 */
626 getRootState(): NavigationState;
627 /**
628 * Get the currently focused navigation route.
629 */
630 getCurrentRoute(): Route<string> | undefined;
631 /**
632 * Get the currently focused route's options.
633 */
634 getCurrentOptions(): object | undefined;
635 /**
636 * Whether the navigation container is ready to handle actions.
637 */
638 isReady(): boolean;
639 };
640
641export type NavigationContainerRefWithCurrent<ParamList extends {}> =
642 NavigationContainerRef<ParamList> & {
643 current: NavigationContainerRef<ParamList> | null;
644 };
645
646export type TypedNavigator<
647 ParamList extends ParamListBase,
648 State extends NavigationState,
649 ScreenOptions extends {},
650 EventMap extends EventMapBase,
651 Navigator extends React.ComponentType<any>
652> = {
653 /**
654 * Navigator component which manages the child screens.
655 */
656 Navigator: React.ComponentType<
657 Omit<
658 React.ComponentProps<Navigator>,
659 keyof DefaultNavigatorOptions<any, any, any, any>
660 > &
661 DefaultNavigatorOptions<ParamList, State, ScreenOptions, EventMap>
662 >;
663 /**
664 * Component used for grouping multiple route configuration.
665 */
666 Group: React.ComponentType<RouteGroupConfig<ParamList, ScreenOptions>>;
667 /**
668 * Component used for specifying route configuration.
669 */
670 Screen: <RouteName extends keyof ParamList>(
671 _: RouteConfig<ParamList, RouteName, State, ScreenOptions, EventMap>
672 ) => null;
673};
674
675export type NavigatorScreenParams<
676 ParamList,
677 State extends NavigationState = NavigationState
678> =
679 | {
680 screen?: never;
681 params?: never;
682 initial?: never;
683 path?: string;
684 state: PartialState<State> | State | undefined;
685 }
686 | {
687 [RouteName in keyof ParamList]: undefined extends ParamList[RouteName]
688 ? {
689 screen: RouteName;
690 params?: ParamList[RouteName];
691 initial?: boolean;
692 path?: string;
693 state?: never;
694 }
695 : {
696 screen: RouteName;
697 params: ParamList[RouteName];
698 initial?: boolean;
699 path?: string;
700 state?: never;
701 };
702 }[keyof ParamList];
703
704export type PathConfig<ParamList extends {}> = {
705 path?: string;
706 exact?: boolean;
707 parse?: Record<string, (value: string) => any>;
708 stringify?: Record<string, (value: any) => string>;
709 screens?: PathConfigMap<ParamList>;
710 initialRouteName?: keyof ParamList;
711};
712
713export type PathConfigMap<ParamList extends {}> = {
714 [RouteName in keyof ParamList]?: NonNullable<
715 ParamList[RouteName]
716 > extends NavigatorScreenParams<infer T, any>
717 ? string | PathConfig<T>
718 : string | Omit<PathConfig<{}>, 'screens' | 'initialRouteName'>;
719};