Monorepo双应用中如何根据App Slug动态匹配React Native屏幕Props类型?
解决Monorepo中共享Auth屏幕的动态Props类型问题
问题根源
你之前的写法无法工作,核心原因是:TypeScript的类型系统是静态编译时的,而appSlug是运行时才能确定的变量,编译阶段TypeScript无法知道appSlug的具体值,因此无法用它来做类型分支判断。
正确实现方案
方案1:利用编译时环境变量(推荐)
因为Expo的slug在编译阶段就已经确定(每个应用的app.json/app.config.ts里的配置),所以可以通过注入编译时环境变量让TypeScript提前知道当前应用类型:
给各应用配置编译时常量
在client应用的app.config.ts中添加:export default { expo: { slug: 'client', // 其他配置 extra: { APP_ENV: 'client', }, }, };商家端同理,把
APP_ENV设为merchant。在共享包中定义条件类型
在共享auth包的类型文件(比如types/auth.ts)中:import type { ClientMyScreenProps } from '@your-monorepo/client/types'; import type { MerchantMyScreenProps } from '@your-monorepo/merchant/types'; // 声明编译时常量,由各应用注入 declare const APP_ENV: 'client' | 'merchant'; // 根据APP_ENV自动推断对应的Props类型 export type MyScreenProps = APP_ENV extends 'client' ? ClientMyScreenProps : MerchantMyScreenProps;在屏幕组件中使用类型
import type { MyScreenProps } from '../types/auth'; export const RequestEmailLinkScreen = ({ route, navigation }: MyScreenProps) => { // 组件逻辑 };编译时TypeScript会根据当前应用的
APP_ENV值自动生成对应类型,完全不需要运行时判断。
方案2:运行时判断+类型断言(兼容场景)
如果必须依赖运行时的appSlug,可以通过类型断言结合运行时分支缩小类型范围:
import { MyScreenProps as ClientMyScreenProps } from '@your-monorepo/client/types'; import { MyScreenProps as MerchantMyScreenProps } from '@your-monorepo/merchant/types'; import Constants from 'expo-constants'; // 联合类型覆盖两种场景 type Props = ClientMyScreenProps | MerchantMyScreenProps; export const RequestEmailLinkScreen = ({ route, navigation }: Props) => { const appSlug = Constants.expoConfig?.slug; if (appSlug === 'client') { // 手动断言为客户端类型,TypeScript会自动识别分支内的类型 const clientNavigation = navigation as ClientMyScreenProps['navigation']; const clientRoute = route as ClientMyScreenProps['route']; // 客户端专属逻辑 } else { const merchantNavigation = navigation as MerchantMyScreenProps['navigation']; const merchantRoute = route as MerchantMyScreenProps['route']; // 商家端专属逻辑 } };
更优的Monorepo架构方案
从架构层面优化,避免类型分支判断,通过泛型+基础类型继承实现类型复用:
共享包定义基础泛型类型
在共享auth包的types/auth.ts中定义通用的Auth屏幕类型:import type { StackNavigationProp, RouteProp } from '@react-navigation/stack'; // 所有Auth屏幕的公共参数列表 export type AuthScreenParams = { RequestEmailLink: { email?: string }; // 其他Auth屏幕参数 }; // 泛型Props,接收各应用的根导航器参数列表作为输入 export type AuthScreenProps<RootStackParamList> = { navigation: StackNavigationProp<RootStackParamList, keyof AuthScreenParams>; route: RouteProp<AuthScreenParams, keyof AuthScreenParams>; };各应用定义专属类型
client应用的类型文件:import type { AuthScreenProps } from '@your-monorepo/auth/types'; import type { ClientRootStackParamList } from './root-stack'; // 客户端专属的Auth屏幕Props export type ClientAuthScreenProps = AuthScreenProps<ClientRootStackParamList>;商家端同理定义
MerchantAuthScreenProps。共享组件使用泛型
import type { AuthScreenProps } from '../types/auth'; // 用泛型接收具体的根导航器类型 export const RequestEmailLinkScreen = <T>({ route, navigation }: AuthScreenProps<T>) => { // 组件逻辑,类型会自动适配传入的T };这种方式完全不需要判断应用类型,类型安全且架构清晰,适合长期维护。
内容的提问来源于stack exchange,提问作者Ben
相关产品推荐
相关产品推荐

