React Navigation (Native) + TypeScript:Tab父导航嵌套Stack子导航时复合屏幕Props的导航类型异常问题
Let's break down your questions one by one, with practical fixes for the typing issue you're facing:
1. Why is route typed correctly but navigation is any?
This usually happens when you don't fully compose the navigation types from both your parent Tab Navigator and child Stack Navigator using CompositeScreenProps.
When using CompositeScreenProps, you need to explicitly pass both the child navigator's screen props (your Home Stack) and the parent navigator's screen props (your Tab Navigator). If you only define route types from the Stack but fail to properly link navigation types from both navigators, TypeScript will fall back to any for the untyped navigation prop. Another common culprit is missing clear parameter list definitions for your navigators—without defining what screens and params each navigator handles, TypeScript can't infer correct navigation types.
2. Is it a valid pattern to render a Stack Navigator inside a Tab Navigator?
Absolutely! This is one of the most common and recommended patterns in React Navigation.
Having each Tab contain its own Stack Navigator lets you maintain independent navigation histories for each tab. For example, if you navigate from Home to Person in the Home tab, switching to the AddPerson tab and back will keep you on the Person screen—this is the expected behavior for most apps. React Navigation fully supports this nested structure, so you're following a standard, production-ready practice here.
3. How to properly type the navigation prop?
Here's a step-by-step fix to get fully typed navigation and route props:
Step 1: Define your navigator parameter lists
First, explicitly define the screens and their params for both your Tab and Stack navigators:
// Params for the Home Stack screens type HomeStackParamList = { Home: undefined; // No params needed for Home screen Person: { personId?: string }; // Example param for Person screen }; // Params for the Tab Navigator screens type TabParamList = { HomeStack: undefined; AddPerson: undefined; };
Step 2: Create typed navigator instances
Use the parameter lists to create strongly-typed navigators:
import { createStackNavigator } from '@react-navigation/stack'; import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; const HomeStack = createStackNavigator<HomeStackParamList>(); const Tab = createBottomTabNavigator<TabParamList>();
Step 3: Use CompositeScreenProps in your screen components
In screens nested inside the Home Stack (like HomeScreen or PersonScreen), combine the Stack's screen props with the Tab's screen props:
import { CompositeScreenProps, StackScreenProps } from '@react-navigation/stack'; import { BottomTabScreenProps } from '@react-navigation/bottom-tabs'; // Combined props type for screens in the Home Stack type HomeScreenProps = CompositeScreenProps< StackScreenProps<HomeStackParamList, 'Home'>, BottomTabScreenProps<TabParamList> >; // Now use this type in your component const HomeScreen = ({ navigation, route }: HomeScreenProps) => { // Navigation is fully typed! TypeScript will catch invalid calls const goToAddPerson = () => navigation.navigate('AddPerson'); const goToPerson = () => navigation.navigate('Person', { personId: '123' }); return <View>{/* Your screen content */}</View>; };
Step 4: Finalize your nested navigator setup
Your existing HomeStackScreen doesn't need extra typing now that we've typed the navigator itself:
const HomeStackScreen = () => ( <HomeStack.Navigator> <HomeStack.Screen name="Home" component={HomeScreen} /> <HomeStack.Screen name="Person" component={PersonScreen} /> </HomeStack.Navigator> ); // Tab Navigator with typed screens const AppNavigator = () => ( <Tab.Navigator> <Tab.Screen name="HomeStack" component={HomeStackScreen} /> <Tab.Screen name="AddPerson" component={AddPersonScreen} /> </Tab.Navigator> );
With these changes, both navigation and route props will be fully typed, and TypeScript will flag any invalid navigation actions (like navigating to a non-existent screen or passing incorrect params).
内容的提问来源于stack exchange,提问作者Dan Page

