繁体   English   中英

将具有两种泛型类型的泛型接口组合为仅具有一种泛型类型的一种类型

[英]Combine generic interfaces with two generic types into one type with only one generic type

就我而言,我正在使用React的useReducer ,但我的问题是纯打字稿问题。

采取以下措施:

export interface State<T> {
  values: T;
  someOtherProp: boolean;
}

export interface ActionOne<T, K extends keyof T> {
  type: "UPDATE_VALUE_ONE";
  payload: { name: K; value: T[K] };
}


export interface ActionTwo<T, K extends keyof T> {
  type: "UPDATE_VALUE_TWO";
  payload: { name: K; value: T[K] };
}

现在,将两个操作接口合并为一个联合类型:

type ActionTypes<T, K extends keyof T> = ActionOne<T, K> | ActionTwo<T, K>

在下一步中,函数将使用ActionTypes作为其参数的类型:

export const reducer = <T>(
  state: State<T>,
  action: ActionTypes<T> // This already breaks because the second generic type is missing
): State<T> =>
{ 
   switch (action.type) {
     case "UPDATE_VALUE_ONE":
      return {
        ...state,
        values: { ...state.values, [action.payload.name]: action.payload.value}
      };
     case "UPDATE_VALUE_ONE":
    // ...
  }
}

正如您在代码的注释中看到的那样, ActionTypes<T>已经损坏。 就我而言,我不希望reducer函数担心ActionTypes<T, K>类型K

我知道我可以将keyof T添加到action: ActionTypes<T, keyof T>但是这会破坏此行的类型检查:

values: { ...state.values, [action.payload.name]: action.payload.value}

因为[action.payload.name]类型可能不同于action.payload.value

实际上,如果ActionType<T, K extends keyof T>仅具有一个通用类型( TActionType<T, K extends keyof T>那将是更加理想的选择。

如果ActionOneActionTwo是由函数返回的,而不是将它们传递为目标,则可以通过内联声明这些函数来工作:

type ActionTypes<T> =
 | (<T, K extends keyof T>() => ActionOne<T, K>)
 | (<T, K extends keyof T>() => ActionTwo<T, K>);`

当在仅声明一个通用类型T另一个接口/类型中使用接口的第二通用类型K的信息时,如何“隐藏”该信息? (特别是如果K仍然是T的依赖项,则K extends keyof T

对于泛型函数,这可以通过内联声明解决,那么如何为接口解决呢?

在这种情况下,似乎您希望action参数是所有可能的动作类型的并集...对于keyof T每个可能的K 如果是这样,我将使用分布式条件类型keyof T的并集keyof T分解为每个单独的文字K ,并获取所有ActionTypes<T, K>

type SomeActionOne<T, K extends keyof T = keyof T> = K extends any
  ? ActionOne<T, K>
  : never;
type SomeActionTwo<T, K extends keyof T = keyof T> = K extends any
  ? ActionTwo<T, K>
  : never;
type AllPossibleActionTypes<T> = SomeActionOne<T> | SomeActionTwo<T>;

(更新:我改性的上述版本AllPossibleActionTypes<T>到分布在ActionOne<T, K>对于所有K ,然后分别分配ActionTwo<T, K>对于所有K ,然后统一它们事后注意,这是。当您指定具体类型T ,它的类型完全相同 ,但是当T仍是一个无法解析的泛型时,编译器会对此做出不同的解释,特别是在旧版本中,编译器将等待,直到知道T将类型分为ActionOneActionTwo品种;现在编译器从一开始就知道这一点。)

那么reducer的签名将是:

export const reducer = <T>(
  state: State<T>,
  action: AllPossibleActionTypes<T>
): State<T> => { ... }

而且您可以看到在调用reducer()时得到了合理的提示:

reducer(
  { values: { a: "a", b: 1, c: true }, someOtherProp: true },
  { type: "UPDATE_VALUE_TWO", payload: { name: "b", value: 3 } }
); // hinted for value: number when you type in "b" for name

好的,希望能有所帮助; 祝好运!

链接到代码

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM