reactjs - 将具有两种泛型类型的泛型接口组合成一种仅具有一种泛型类型的类型
问题描述
就我而言,我正在使用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
函数担心是什么类型K
。ActionTypes<T, K>
我知道我可以添加keyof T
,action: ActionTypes<T, keyof T>
但这会破坏这一行的类型检查:
values: { ...state.values, [action.payload.name]: action.payload.value}
因为[action.payload.name]
cane的类型不同于action.payload.value
ActionType<T, K extends keyof T>
实际上,如果只有一种泛型类型 ( )会更理想T
。
如果ActionOne
和ActionTwo
由函数返回,而不是直接将它们作为对象传递,它会通过声明函数内联来工作:
type ActionTypes<T> =
| (<T, K extends keyof T>() => ActionOne<T, K>)
| (<T, K extends keyof T>() => ActionTwo<T, K>);`
问题
K
当接口的第二种泛型类型用于仅声明一个泛型类型的另一个接口/类型时,如何“隐藏”该接口的第二个泛型类型的信息T
?(特别是如果K
无论如何都依赖于T
。K extends keyof T
)
对于泛型函数,这可以通过内联声明来解决,那么接口如何解决呢?
解决方案
在这种情况下,您似乎希望action
参数是所有可能的动作类型的联合......对于每个可能K
的 in 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>;
(更新:我修改了上面的版本为 allAllPossibleActionTypes<T>
分发,然后分别为 all分发,然后将它们统一。请注意,当您指定具体类型时,这是完全相同的类型,但 when仍然是未解决的泛型编译器对它们的解释不同。具体来说,在旧版本中,编译器会等到它知道将类型拆分为和变体;现在编译器从一开始就知道这一点。)ActionOne<T, K>
K
ActionTwo<T, K>
K
T
T
T
ActionOne
ActionTwo
那么签名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
好的,希望有帮助;祝你好运!
推荐阅读
- r - grid.arrange 与 ggplotly
- javascript - 在 C3 Js 图表中首次加载页面时启用/禁用图例元素。此外,管理图例元素基于复选框值启用/禁用
- javascript - 非法参数 bcrypt js - mongodb & node
- android-studio-3.0 - 如何使用 4 位数字从 Android 应用程序发送短信
- python - 对包含 ReferenceField 的 ListField 进行排序(Mongoengine)
- java - 无法单击 Dynamics CRM selenium java 中的功能区按钮
- c - 我的 Keil v5 编译器有什么问题,有时会创建目标,有时不会创建目标?
- mysql - 如何在某个日期之后选择 MAX(date)
- vuejs2 - Vuex 状态和吸气剂未在 Vue cli 3 中检索存储数据
- javascript - 使用 'value' 或 'textContent' 属性更安全吗?