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

如何在泛型参数为联合类型时保持函数参数元组的类型关联

TypeScript关联联合类型问题修复

问题场景

泛型函数接收关联的screen和value参数,需要传入第三方定义的navigate函数,但当泛型参数为联合类型时,TypeScript无法追踪参数对的关联关系,导致类型不匹配错误,需在不使用any的前提下解决。

第三方不可修改类型代码

// --- 第三方定义的类型,无法修改 ---
export type ParamListBase = Record<string, object | undefined>;

type ScreenParamsPair<
  ParamList extends ParamListBase,
  RouteName extends keyof ParamList,
> = {
  [Screen in keyof ParamList]: undefined extends ParamList[Screen]
    ?
      | [screen: Screen]
      | [screen: Screen, params: ParamList[Screen]]
    : [screen: Screen, params: ParamList[Screen]];
}[RouteName];

type NavigationHelpersCommon<
  ParamList extends ParamListBase,
> = {
  navigate<RouteName extends keyof ParamList>(
    ...args: ScreenParamsPair<ParamList, RouteName>
  ): void;
}

type NavigationProp<ParamList extends {}> = NavigationHelpersCommon<ParamList>

function useNavigation<T = NavigationHelpersCommon<{}>>(): T {
  return {} as T;
}
// --- 第三方类型结束 ---

自定义类型与泛型函数

type Obj1 = {
  field1: string;
  field2: string;
  field3: string;
}
type Obj2 = {
  field1: string;
  field2: string;
}
type Obj3 = {
  field1: string;
  field2: string;
  field3: `#${string}`
}

export type AppStackParamList = {
  Screen1: { value: string | null };
  Screen2: { value: Obj1 | null };
  Screen3: { value: Obj2 | null };
  Screen4: { value: Obj3 | null };
}

type ScreenValueMap = {
  'Screen1': 'string',
  'Screen2': { field1: '213', field2: '123', field3: '512' },
  'Screen3': { field1: '213', field2: '123' },
}

type Props<T extends keyof ScreenValueMap> = {
  screen: T,
  value: ScreenValueMap[T]
}

const genericFunction = <T extends keyof ScreenValueMap>({ screen, value }: Props<T>) => {
  const navigation = useNavigation<NavigationProp<AppStackParamList>>();
  navigation.navigate(screen, { value }); // 类型错误
  navigation.navigate(screen, { value: null }); // 同样类型错误
}

错误信息

Argument of type '[keyof ScreenValueMap, { value: "string" | { field1: "213"; field2: "123"; field3: "512"; } | { field1: "213"; field2: "123"; }; }]' is not assignable to parameter of type 'ScreenParamsPair<AppStackParamList, keyof AppStackParamList>'.
  Type '[keyof ScreenValueMap, { value: "string" | { field1: "213"; field2: "123"; field3: "512"; } | { field1: "213"; field2: "123"; }; }]' is not assignable to type '[screen: "Screen1", params: { value: string | null; }] | [screen: "Screen2", params: { value: Obj1 | null; }] | [screen: "Screen3", params: { value: Obj2 | null; }]'.
    Type '[keyof ScreenValueMap, { value: "string" | { field1: "213"; field2: "123"; field3: "512"; } | { field1: "213"; field2: "123"; }; }]' is not assignable to type '[screen: "Screen3", params: { value: Obj2 | null; }]'.
      Type at position 0 in source is not compatible with type at position 0 in target.
        Type 'keyof ScreenValueMap' is not assignable to type '"Screen3"'.
          Type '"Screen1"' is not assignable to type '"Screen3"'.(2345)

解决方案

方案1:使用联合类型参数替代泛型

直接定义包含关联参数对的联合类型,让TypeScript自动识别每个参数的匹配关系:

// 定义screen与对应参数的关联联合类型
type ScreenParamPair = {
  [K in keyof ScreenValueMap]: {
    screen: K;
    value: ScreenValueMap[K];
  };
}[keyof ScreenValueMap];

const genericFunction = (props: ScreenParamPair) => {
  const navigation = useNavigation<NavigationProp<AppStackParamList>>();
  // TypeScript能正确追踪screen和value的关联
  navigation.navigate(props.screen, { value: props.value });
  navigation.navigate(props.screen, { value: null });
};

优点:完全保留类型安全性,无需断言,TypeScript自动推导关联关系。
缺点:函数不再是泛型,无法直接针对单个类型做特定处理。

方案2:泛型+安全类型断言

保留泛型的同时,通过类型断言明确告知TypeScript参数的兼容性,确保断言的安全性:

const genericFunction = <T extends keyof ScreenValueMap>({ screen, value }: Props<T>) => {
  const navigation = useNavigation<NavigationProp<AppStackParamList>>();
  // 断言{ value }为AppStackParamList[T],因ScreenValueMap[T]与AppStackParamList[T]['value']非null部分完全兼容
  navigation.navigate(screen, { value } as AppStackParamList[T]);
  navigation.navigate(screen, { value: null } as AppStackParamList[T]);
};

优点:保留泛型的灵活性,同时避免使用any。
缺点:需手动确保ScreenValueMap与AppStackParamList的类型匹配,后续修改需同步维护两者一致性。

方案3:添加类型约束强化关联

通过映射类型明确ScreenValueMap与AppStackParamList的对应关系,让泛型约束更严格:

// 定义ScreenValueMap必须符合AppStackParamList的非null值类型
type ValidScreenValueMap = {
  [K in keyof AppStackParamList]: Exclude<AppStackParamList[K]['value'], null>;
};

// 约束ScreenValueMap必须匹配ValidScreenValueMap的子集
type ScreenValueMap = Pick<ValidScreenValueMap, 'Screen1' | 'Screen2' | 'Screen3'>;

type Props<T extends keyof ScreenValueMap> = {
  screen: T,
  value: ScreenValueMap[T]
}

const genericFunction = <T extends keyof ScreenValueMap>({ screen, value }: Props<T>) => {
  const navigation = useNavigation<NavigationProp<AppStackParamList>>();
  // TypeScript能自动推断{ value }符合AppStackParamList[T]
  navigation.navigate(screen, { value });
  navigation.navigate(screen, { value: null });
};

优点:从根源上确保ScreenValueMap与AppStackParamList的类型一致性,后续修改时自动检查,避免类型不匹配。
缺点:需要调整ScreenValueMap的定义方式,依赖AppStackParamList的结构。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 21:14:59