diff --git a/packages/bottom-tabs/src/types.tsx b/packages/bottom-tabs/src/types.tsx index 7015ac40..b33af36e 100644 --- a/packages/bottom-tabs/src/types.tsx +++ b/packages/bottom-tabs/src/types.tsx @@ -153,15 +153,12 @@ export type BottomTabNavigationOptions = { }; export type BottomTabDescriptor = Descriptor< - ParamListBase, - string, - TabNavigationState, - BottomTabNavigationOptions + BottomTabNavigationOptions, + BottomTabNavigationProp, + RouteProp >; -export type BottomTabDescriptorMap = { - [key: string]: BottomTabDescriptor; -}; +export type BottomTabDescriptorMap = Record; export type BottomTabNavigationConfig = { /** diff --git a/packages/core/src/types.tsx b/packages/core/src/types.tsx index 65d1dadd..c84a7cd2 100644 --- a/packages/core/src/types.tsx +++ b/packages/core/src/types.tsx @@ -318,11 +318,9 @@ export type CompositeNavigationProp< >; export type Descriptor< - ParamList extends ParamListBase, - RouteName extends keyof ParamList = string, - State extends NavigationState = NavigationState, - ScreenOptions extends {} = {}, - EventMap extends EventMapBase = {} + ScreenOptions extends {}, + Navigation extends NavigationProp, + Route extends RouteProp > = { /** * Render the component associated with this route. @@ -337,18 +335,12 @@ export type Descriptor< /** * Route object for the screen */ - route: RouteProp; + route: Route; /** * Navigation object for the screen */ - navigation: NavigationProp< - ParamList, - RouteName, - State, - ScreenOptions, - EventMap - >; + navigation: Navigation; }; export type ScreenListeners< diff --git a/packages/core/src/useCurrentRender.tsx b/packages/core/src/useCurrentRender.tsx index 168f5b54..abeb7fa9 100644 --- a/packages/core/src/useCurrentRender.tsx +++ b/packages/core/src/useCurrentRender.tsx @@ -1,14 +1,24 @@ import * as React from 'react'; import type { NavigationState, ParamListBase } from '@react-navigation/routers'; import CurrentRenderContext from './CurrentRenderContext'; -import type { Descriptor, NavigationHelpers } from './types'; +import type { + Descriptor, + NavigationHelpers, + NavigationProp, + RouteProp, +} from './types'; type Options = { state: NavigationState; navigation: NavigationHelpers; - descriptors: { - [key: string]: Descriptor; - }; + descriptors: Record< + string, + Descriptor< + object, + NavigationProp, + RouteProp + > + >; }; /** diff --git a/packages/core/src/useDescriptors.tsx b/packages/core/src/useDescriptors.tsx index c77ccd6c..5242415a 100644 --- a/packages/core/src/useDescriptors.tsx +++ b/packages/core/src/useDescriptors.tsx @@ -21,6 +21,7 @@ import type { RouteConfig, RouteProp, EventMapBase, + NavigationProp, } from './types'; type Options< @@ -50,7 +51,7 @@ type Options< addKeyedListener: AddKeyedListener; onRouteFocus: (key: string) => void; router: Router; - emitter: NavigationEventEmitter; + emitter: NavigationEventEmitter; }; /** @@ -63,6 +64,7 @@ type Options< */ export default function useDescriptors< State extends NavigationState, + ActionHelpers extends Record void>, ScreenOptions extends {}, EventMap extends EventMapBase >({ @@ -105,7 +107,7 @@ export default function useDescriptors< ] ); - const navigations = useNavigationCache({ + const navigations = useNavigationCache({ state, getState, navigation, @@ -117,7 +119,15 @@ export default function useDescriptors< const routes = useRouteCache(state.routes); return routes.reduce< - Record> + Record< + string, + Descriptor< + ScreenOptions, + NavigationProp & + ActionHelpers, + RouteProp + > + > >((acc, route, i) => { const screen = screens[route.name]; const navigation = navigations[route.key]; @@ -145,6 +155,7 @@ export default function useDescriptors< acc[route.key] = { route, + // @ts-expect-error: it's missing action helpers, fix later navigation, render() { return ( diff --git a/packages/core/src/useNavigationBuilder.tsx b/packages/core/src/useNavigationBuilder.tsx index 268c8de9..82370951 100644 --- a/packages/core/src/useNavigationBuilder.tsx +++ b/packages/core/src/useNavigationBuilder.tsx @@ -533,7 +533,12 @@ export default function useNavigationBuilder< getStateListeners: keyedListeners.getState, }); - const descriptors = useDescriptors({ + const descriptors = useDescriptors< + State, + ActionHelpers, + ScreenOptions, + EventMap + >({ state, screens, navigation, @@ -545,6 +550,7 @@ export default function useNavigationBuilder< addListener, addKeyedListener, router, + // @ts-expect-error: this should have both core and custom events, but too much work right now emitter, }); diff --git a/packages/core/src/useNavigationCache.tsx b/packages/core/src/useNavigationCache.tsx index fac9fa74..6531abe3 100644 --- a/packages/core/src/useNavigationCache.tsx +++ b/packages/core/src/useNavigationCache.tsx @@ -8,9 +8,12 @@ import { } from '@react-navigation/routers'; import type { NavigationEventEmitter } from './useEventEmitter'; -import type { EventMapBase, NavigationHelpers, NavigationProp } from './types'; +import type { NavigationHelpers, NavigationProp } from './types'; -type Options = { +type Options< + State extends NavigationState, + EventMap extends Record +> = { state: State; getState: () => State; navigation: NavigationHelpers & @@ -19,15 +22,17 @@ type Options = { cb: (options: Record) => Record ) => void; router: Router; - emitter: NavigationEventEmitter; + emitter: NavigationEventEmitter; }; type NavigationCache< State extends NavigationState, - ScreenOptions extends {} -> = { - [key: string]: NavigationProp; -}; + ScreenOptions extends {}, + EventMap extends Record +> = Record< + string, + NavigationProp +>; /** * Hook to cache navigation objects for each screen in the navigator. @@ -36,7 +41,8 @@ type NavigationCache< */ export default function useNavigationCache< State extends NavigationState, - ScreenOptions extends {} + ScreenOptions extends {}, + EventMap extends Record >({ state, getState, @@ -44,12 +50,12 @@ export default function useNavigationCache< setOptions, router, emitter, -}: Options) { +}: Options) { // Cache object which holds navigation objects for each screen // We use `React.useMemo` instead of `React.useRef` coz we want to invalidate it when deps change // In reality, these deps will rarely change, if ever const cache = React.useMemo( - () => ({ current: {} as NavigationCache }), + () => ({ current: {} as NavigationCache }), // eslint-disable-next-line react-hooks/exhaustive-deps [getState, navigation, setOptions, router, emitter] ); @@ -59,67 +65,67 @@ export default function useNavigationCache< ...CommonActions, }; - cache.current = state.routes.reduce>( - (acc, route) => { - const previous = cache.current[route.key]; + cache.current = state.routes.reduce< + NavigationCache + >((acc, route) => { + const previous = cache.current[route.key]; - if (previous) { - // If a cached navigation object already exists, reuse it - acc[route.key] = previous; - } else { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { emit, ...rest } = navigation; + if (previous) { + // If a cached navigation object already exists, reuse it + acc[route.key] = previous; + } else { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { emit, ...rest } = navigation; - const dispatch = ( - action: NavigationAction | ((state: State) => NavigationAction) - ) => { - const payload = - typeof action === 'function' ? action(getState()) : action; + const dispatch = ( + action: NavigationAction | ((state: State) => NavigationAction) + ) => { + const payload = + typeof action === 'function' ? action(getState()) : action; - navigation.dispatch( - typeof payload === 'object' && payload != null - ? { source: route.key, ...payload } - : payload - ); - }; - - const helpers = Object.keys(actions).reduce void>>( - (acc, name) => { - // @ts-expect-error: name is a valid key, but TypeScript is dumb - acc[name] = (...args: any) => dispatch(actions[name](...args)); - return acc; - }, - {} + navigation.dispatch( + typeof payload === 'object' && payload != null + ? { source: route.key, ...payload } + : payload ); + }; - acc[route.key] = { - ...rest, - ...helpers, - ...emitter.create(route.key), - dispatch, - setOptions: (options: object) => - setOptions((o) => ({ - ...o, - [route.key]: { ...o[route.key], ...options }, - })), - isFocused: () => { - const state = getState(); + const helpers = Object.keys(actions).reduce void>>( + (acc, name) => { + // @ts-expect-error: name is a valid key, but TypeScript is dumb + acc[name] = (...args: any) => dispatch(actions[name](...args)); + return acc; + }, + {} + ); - if (state.routes[state.index].key !== route.key) { - return false; - } + acc[route.key] = { + ...rest, + ...helpers, + // FIXME: too much work to fix the types for now + ...(emitter.create(route.key) as any), + dispatch, + setOptions: (options: object) => + setOptions((o) => ({ + ...o, + [route.key]: { ...o[route.key], ...options }, + })), + isFocused: () => { + const state = getState(); - // If the current screen is focused, we also need to check if parent navigator is focused - // This makes sure that we return the focus state in the whole tree, not just this navigator - return navigation ? navigation.isFocused() : true; - }, - }; - } + if (state.routes[state.index].key !== route.key) { + return false; + } - return acc; - }, - {} - ); + // If the current screen is focused, we also need to check if parent navigator is focused + // This makes sure that we return the focus state in the whole tree, not just this navigator + return navigation ? navigation.isFocused() : true; + }, + }; + } + + return acc; + }, {}); return cache.current; } diff --git a/packages/drawer/src/types.tsx b/packages/drawer/src/types.tsx index bc36631d..0cb66c3b 100644 --- a/packages/drawer/src/types.tsx +++ b/packages/drawer/src/types.tsx @@ -284,7 +284,7 @@ export type DrawerHeaderProps = { /** * Route object for the current screen. */ - route: Route; + route: RouteProp; /** * Navigation prop for the header. */ @@ -329,12 +329,9 @@ export type DrawerScreenProps< }; export type DrawerDescriptor = Descriptor< - ParamListBase, - string, - DrawerNavigationState, - DrawerNavigationOptions + DrawerNavigationOptions, + DrawerNavigationProp, + RouteProp >; -export type DrawerDescriptorMap = { - [key: string]: DrawerDescriptor; -}; +export type DrawerDescriptorMap = Record; diff --git a/packages/material-bottom-tabs/src/types.tsx b/packages/material-bottom-tabs/src/types.tsx index b410b2f5..75d82942 100644 --- a/packages/material-bottom-tabs/src/types.tsx +++ b/packages/material-bottom-tabs/src/types.tsx @@ -83,15 +83,15 @@ export type MaterialBottomTabNavigationOptions = { }; export type MaterialBottomTabDescriptor = Descriptor< - ParamListBase, - string, - TabNavigationState, - MaterialBottomTabNavigationOptions + MaterialBottomTabNavigationOptions, + MaterialBottomTabNavigationProp, + RouteProp >; -export type MaterialBottomTabDescriptorMap = { - [key: string]: MaterialBottomTabDescriptor; -}; +export type MaterialBottomTabDescriptorMap = Record< + string, + MaterialBottomTabDescriptor +>; export type MaterialBottomTabNavigationConfig = Partial< Omit< diff --git a/packages/material-top-tabs/src/types.tsx b/packages/material-top-tabs/src/types.tsx index 60270ff7..33d58720 100644 --- a/packages/material-top-tabs/src/types.tsx +++ b/packages/material-top-tabs/src/types.tsx @@ -93,15 +93,15 @@ export type MaterialTopTabNavigationOptions = { }; export type MaterialTopTabDescriptor = Descriptor< - ParamListBase, - string, - TabNavigationState, - MaterialTopTabNavigationOptions + MaterialTopTabNavigationOptions, + MaterialTopTabNavigationProp, + RouteProp >; -export type MaterialTopTabDescriptorMap = { - [key: string]: MaterialTopTabDescriptor; -}; +export type MaterialTopTabDescriptorMap = Record< + string, + MaterialTopTabDescriptor +>; export type MaterialTopTabNavigationConfig = Partial< Omit< diff --git a/packages/stack/src/types.tsx b/packages/stack/src/types.tsx index ed247437..959de302 100644 --- a/packages/stack/src/types.tsx +++ b/packages/stack/src/types.tsx @@ -238,7 +238,7 @@ export type StackHeaderProps = { /** * Route object for the current screen. */ - route: Route; + route: RouteProp; /** * Animated nodes representing the progress of the animation of the previous screen. */ @@ -267,15 +267,12 @@ export type StackHeaderProps = { }; export type StackDescriptor = Descriptor< - ParamListBase, - string, - StackNavigationState, - StackNavigationOptions + StackNavigationOptions, + StackNavigationProp, + RouteProp >; -export type StackDescriptorMap = { - [key: string]: StackDescriptor; -}; +export type StackDescriptorMap = Record; export type StackNavigationOptions = StackHeaderOptions & Partial & { diff --git a/packages/stack/src/views/Stack/StackView.tsx b/packages/stack/src/views/Stack/StackView.tsx index 0dfbdbf8..c13b891c 100644 --- a/packages/stack/src/views/Stack/StackView.tsx +++ b/packages/stack/src/views/Stack/StackView.tsx @@ -16,12 +16,12 @@ import HeaderContainer, { Props as HeaderContainerProps, } from '../Header/HeaderContainer'; import SafeAreaProviderCompat from '../SafeAreaProviderCompat'; +import HeaderShownContext from '../../utils/HeaderShownContext'; import type { StackNavigationHelpers, StackNavigationConfig, StackDescriptorMap, } from '../../types'; -import HeaderShownContext from '../../utils/HeaderShownContext'; type Props = StackNavigationConfig & { state: StackNavigationState;