简体   繁体   English

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

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

In my case I'm working with useReducer from React but my question is a pure typescript issue. 就我而言,我正在使用React的useReducer ,但我的问题是纯打字稿问题。

Take following: 采取以下措施:

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] };
}

Now the two action interfaces are combined into a single union type: 现在,将两个操作接口合并为一个联合类型:

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

In the next step a function will use ActionTypes as the type of it's parameter: 在下一步中,函数将使用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":
    // ...
  }
}

As you can see in the code's comment ActionTypes<T> is already breaking. 正如您在代码的注释中看到的那样, ActionTypes<T>已经损坏。 In my case I don't want the reducer function to worry about what type K of ActionTypes<T, K> is. 就我而言,我不希望reducer函数担心ActionTypes<T, K>类型K

I know I could add keyof T to action: ActionTypes<T, keyof T> but ths would break the typechecking for this line: 我知道我可以将keyof T添加到action: ActionTypes<T, keyof T>但是这会破坏此行的类型检查:

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

because the type of [action.payload.name] coulde be different from action.payload.value 因为[action.payload.name]类型可能不同于action.payload.value

Actually it would even be more ideal if ActionType<T, K extends keyof T> would only have one generic type ( T ). 实际上,如果ActionType<T, K extends keyof T>仅具有一个通用类型( TActionType<T, K extends keyof T>那将是更加理想的选择。

If ActionOne and ActionTwo were returned by a functions instead of passing them direclty as objec t it work would by declaring the functions inline: 如果ActionOneActionTwo是由函数返回的,而不是将它们传递为目标,则可以通过内联声明这些函数来工作:

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

Question

How can the information of a second generic type K of an interface be "hidden" when it is used in another interface/type that only declares one generic type T ? 当在仅声明一个通用类型T另一个接口/类型中使用接口的第二通用类型K的信息时,如何“隐藏”该信息? (Especially if K is anyway dependend on T by K extends keyof T .) (特别是如果K仍然是T的依赖项,则K extends keyof T

For generic functions this can be solved through inline declaration, so how can it be solved for interfaces? 对于泛型函数,这可以通过内联声明解决,那么如何为接口解决呢?

In this case it seems like you would like the action parameter to be the union of all possible action types... for each possible K in keyof T . 在这种情况下,似乎您希望action参数是所有可能的动作类型的并集...对于keyof T每个可能的K If so, I would use a distributive conditional type to break the union keyof T into each individual literal K , and get the union of all of ActionTypes<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>;

(UPDATE: I modified the above version of AllPossibleActionTypes<T> to distribute over ActionOne<T, K> for all K , and then separately distribute ActionTwo<T, K> for all K , and then unify them afterwards. Note that this is exactly the same type when you specify a concrete type T , but when T is still an unresolved generic the compiler reasons differently about them. Specifically, in the old version, the compiler would wait until it knew T to split the types into ActionOne and ActionTwo varieties; now the compiler knows this from the start.) (更新:我改性的上述版本AllPossibleActionTypes<T>到分布在ActionOne<T, K>对于所有K ,然后分别分配ActionTwo<T, K>对于所有K ,然后统一它们事后注意,这是。当您指定具体类型T ,它的类型完全相同 ,但是当T仍是一个无法解析的泛型时,编译器会对此做出不同的解释,特别是在旧版本中,编译器将等待,直到知道T将类型分为ActionOneActionTwo品种;现在编译器从一开始就知道这一点。)

Then the signature of reducer would be: 那么reducer的签名将是:

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

And you could see that you are reasonably hinted when you call reducer() : 而且您可以看到在调用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

Okay, hope that helps; 好的,希望能有所帮助; good luck! 祝好运!

Link to code 链接到代码

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

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