根据通用参数(union)维护传递给函数的参数元组之间的类型相关性

问题描述 投票:0回答:1

当我有一个通用函数,该函数接受彼此依赖的参数(因此它们依赖于泛型)时,我想解决 TypeScript 问题,然后我将这些参数传递给通用函数内的函数。该函数采用一对参数(屏幕和值)。这是示例: 游乐场:https://tsplay.dev/mLV2ew

// --- Start Types here is from third party that I cannot change ---
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;
}
// --- End Types here is from third party that I cannot change ---

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 }); // Error here

  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)

我简化了示例以更易于阅读,但问题是相同的。

在泛型是联合的情况下,它看起来像 Typescript 对屏幕 - 值之间的松散相关性,因此当我将参数传递给导航函数时,我最终会得到一个单独的屏幕联合类型和一个单独的值联合类型屏幕对的并集 - 值。我读了一些有关“相关联合类型”的问题,但我不明白如何解决这个问题。即使类型转换(any 除外)在这里也没有帮助,但我想要一些类型安全,所以 any 不适合我。

typescript typescript-generics union-types
1个回答
0
投票

首先,不应该

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

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

我不清楚如何解决这个问题,但我的猜测是

navigation.navigate
没有足够的泛型适合您的情况

见下面的例子

type O = {
    x:1
    y:2
}
// your case
// your code
const a=<T extends keyof O>(v:T,w:O[T])=>{
  b(v,w) // error!
}
// library code
const b = <T extends keyof O>(...arg:{[k in keyof O]:[k, O[k]]}[T])=>{
}

// fixed!
// your modified code
const a1=<T extends keyof U, U extends O>(v:T,w:U[T])=>{
  b1(v,w) // no error
}
// library modified code
const b1 = <T extends keyof U, U extends O>(...arg:{[k in keyof U]:[k, U[k]]}[T])=>{
}

游乐场

所以看起来它是可以修复的,但我们必须修改第 3 方的类型

© www.soinside.com 2019 - 2024. All rights reserved.