更改对象上属性的位置后获取正确的类型信息

时间:2019-07-10 12:50:44

标签: typescript

我有一个嵌套的选项对象,该对象被某些代码修改并将某些属性上移一个级别。

在这种情况下,我正在努力获得适当的打字稿支持。

function createStore(options) {
  // do stuff
}

// given
const options = {
  state: {},
  modules: {
    buildings: {
      state: {},
      modules: {
        school: {
          state: {}
        },
        cafe: {
          state: {}
        }
      }
    }
  }
};

// type definitions that aren't working
type ObjectInfer<T> = T extends { modules: infer A } ? A : T;
type Store<T> = { [P in keyof T]: ObjectInfer<T[P]> };

// process the options object recursively and move the module definitions up one level
const store = (createStore(options) as unknown) as Store<typeof options>;

console.log(store);

// output:
/* 
{
  state: {},
  buildings: {
    state: {},
    school: {
      state: {}
    },
    cafe: {
      state: {}
    }
  }
}
*/

我希望商店能够如上所示正确键入,但是类型转换不能按预期工作,并且仍然具有与options对象相同的结构。

1 个答案:

答案 0 :(得分:1)

好的,原来的Store<T>的一个问题是,像{[K in keyof T]: XXX}这样的映射类型将具有与T相同的键。因此,如果T具有modules属性,那么Store<T>也是如此。另一个问题是它不是递归的... ObjectInfer<T>提取modules的{​​{1}}属性(如果有的话),但不会归结到这些属性中进行相同的操作。

实际的实现将必须是递归的,并且实际上从其输出中省略了T键。这是一种可行的方法:

modules

在解释这一点之前,让我解释一个等效的类型,但是产生的输出类型更难看:

type Store<T> = T extends { modules: infer A }
  ? (StoreMap<Omit<T, "modules"> & A> extends infer S
      ? { [K in keyof S]: S[K] }
      : never)
  : T;
type StoreMap<T> = { [K in keyof T]: Store<T[K]> };

在这里,您看到type StoreUgly<T> = T extends { modules: infer A } ? StoreUglyMap<Omit<T, "modules"> & A> : T; type StoreUglyMap<T> = { [K in keyof T]: StoreUgly<T[K]> }; 所做的是检查StoreUgly是否具有T属性。如果不是,则返回modules。否则,它将评估T的价值,其中Omit<T, "modules"> & AA属性。 modules在标准库中是defined的映射类型,可从Omit<T, K>的已知键中删除K。因此TOmit<{modules: 1, nodules: 2}, "modules">{nodules: 2}的交集是您尝试做的基本“将Omit<T, "modules"> & A属性向上拉一层”。

但是我们当然需要递归地执行它,所以我们在它上执行modules,它只是将StoreUglyMap<>映射到每个属性。

皱纹是输出是绝对可怕的……如果您执行StoreUgly,您会感到一团糟。因此是StoreUgly<typeof options>版本。如果对象类型很丑Store,有时可以通过编写O来清理它。这只是将O extends infer X ? {[K in keyof X]: X[K]} : never的所有属性映射到它们自己,但效果是更急切地评估属性,而不是将它们留为相交束。

让我们确保输出类型是您想要的:

O

对我很好!希望能有所帮助。祝你好运!

Link to code