在Typescript中索引其他属性的属性

时间:2019-01-15 15:08:43

标签: typescript generics typescript-generics

我有以下几种类型:

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

使用Generics并没有帮助我。在以下示例中,fields的类型为never

type Stores<T> = keyof T;
type Fields<T> = keyof T[Stores<T>];
type Props<TState> = {
    state: Stores<TState>;
    field: Fields<TState>
}

有没有办法做到这一点?

2 个答案:

答案 0 :(得分:1)

您需要为路径中的每个属性使用不同的类型参数。这使编译器可以推理出您指定的特定字段:

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]时,它将得到联合CellsReducer | BarReducer。由于只有工会的普通成员才可以访问,所以keyof (CellsReducer | BarReducer)never(无法访问任何键)。

额外的参数将捕获实际的字段,因此,如果KStore是字符串文字类型"cells" keyof AppState["cells"]将是应用程序状态下该特定字段的键。 KField的工作方式类似,使我们可以正确键入data

为避免两次指定statefield值,您可以编写一个辅助函数:

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
})

答案 1 :(得分:1)

您的意思是:

interface Props<T, K extends keyof T, V extends keyof T[K]> {
    state: keyof T;
    field: T[K];
    data: T[K][V]
}

用法:

const props: Props<AppState, 'cells', 'plan'> = { /* ... */ } ;
const props: Props<AppState, 'bar', 'baz'> = { /* ... */ } ;