You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Monorepo双应用中如何根据App Slug动态匹配React Native屏幕Props类型?

解决Monorepo中共享Auth屏幕的动态Props类型问题

问题根源

你之前的写法无法工作,核心原因是:TypeScript的类型系统是静态编译时的,而appSlug是运行时才能确定的变量,编译阶段TypeScript无法知道appSlug的具体值,因此无法用它来做类型分支判断。

正确实现方案

方案1:利用编译时环境变量(推荐)

因为Expo的slug在编译阶段就已经确定(每个应用的app.json/app.config.ts里的配置),所以可以通过注入编译时环境变量让TypeScript提前知道当前应用类型:

  1. 给各应用配置编译时常量
    在client应用的app.config.ts中添加:

    export default {
      expo: {
        slug: 'client',
        // 其他配置
        extra: {
          APP_ENV: 'client',
        },
      },
    };
    

    商家端同理,把APP_ENV设为merchant。

  2. 在共享包中定义条件类型
    在共享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;
    
  3. 在屏幕组件中使用类型

    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架构方案

从架构层面优化,避免类型分支判断,通过泛型+基础类型继承实现类型复用:

  1. 共享包定义基础泛型类型
    在共享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>;
    };
    
  2. 各应用定义专属类型
    client应用的类型文件:

    import type { AuthScreenProps } from '@your-monorepo/auth/types';
    import type { ClientRootStackParamList } from './root-stack';
    
    // 客户端专属的Auth屏幕Props
    export type ClientAuthScreenProps = AuthScreenProps<ClientRootStackParamList>;
    

    商家端同理定义MerchantAuthScreenProps。

  3. 共享组件使用泛型

    import type { AuthScreenProps } from '../types/auth';
    
    // 用泛型接收具体的根导航器类型
    export const RequestEmailLinkScreen = <T>({ route, navigation }: AuthScreenProps<T>) => {
      // 组件逻辑,类型会自动适配传入的T
    };
    

    这种方式完全不需要判断应用类型,类型安全且架构清晰,适合长期维护。


内容的提问来源于stack exchange,提问作者Ben

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 15:41:47