我有以下类型:
interface CellsReducer {
source: number;
destination: number;
plan: string;
duration: number;
test: []
}
interface BarReducer {
baz: string;
}
interface AppState {
cells: CellsReducer;
bar: BarReducer;
}
我想用以下对象编写一个接口:
interface Props {
store: keyof AppState;
field: // AppState[store]
data: // AppState[store][field]
}
使用泛型并没有给我带来任何好处。
fields
在下面的示例中以类型never
结束:type Stores<T> = keyof T;
type Fields<T> = keyof T[Stores<T>];
type Props<TState> = {
state: Stores<TState>;
field: Fields<TState>
}
有办法吗?
最佳答案
对于路径中的每个属性,需要不同的类型参数。这允许编译器对指定的特定字段进行推理:
type Props<TState, KStore extends keyof TState, KField extends keyof TState[KStore]> = {
state: KStore;
field: KField
data: TState[KStore][KField]
}
let p: Props<AppState, "cells", "duration"> = {
state: "cells",
field: "duration",
data: 1
}
因为当编译器试图展开
AppState[keyof AppState]
时,它将得到一个unionCellsReducer | BarReducer
。因为只有联合的普通成员是可访问的keyof (CellsReducer | BarReducer)
是never
(没有可访问的键)。额外的参数捕获实际字段,因此如果
KStore
字符串字符串类型"cells"
keyof AppState["cells"]
将是App状态中特定字段的键。KField
的工作原理与此类似,允许我们正确键入data
。为了避免两次指定
state
和field
值,可以编写一个helper函数:function propertyFactory<TState>() {
return function <KStore extends keyof TState, KField extends keyof TState[KStore]>(o: Props<TState, KStore, KField>) {
return o;
}
}
let p = propertyFactory<AppState>()({
state: "cells",
field: "duration",
data: 1
})