是否可以将键值接口更改为元组

Is it possible to change an key value interface into a tuple

我需要在这两种类型中提供相同的数据

type DataMap = { 
  id001: 'name1', 
  id002: 'name2', 
  id003: 'name3', 
  ....
} 

type DataTuple = [
  {id: 'id001', name: 'name1'},
  {id: 'id002', name: 'name2'},
  {id: 'id003', name: 'name3'}, 
  ...
]

我想只声明一次这种类型并且有一个单一的事实来源, 但是我无法找出将类型从对象转换为元组的实用类型函数。

理想情况下我想做这样的事情:

type DataMap = { 
  id001: 'name1', 
  id002: 'name2', 
  id003: 'name3', 
  ....
}; 
type DataTuple = MapToTuple<DataMap>;

// or 

type DataTuple = [
  {id: 'id001', name: 'name1'},
  {id: 'id002', name: 'name2'},
  {id: 'id003', name: 'name3'}, 
  ...
];
type DataMap = TupleToMap<DataTuple>;

这些功能 MapToTuple<T>TupleToMap<T> 是否可行?

我有解决方法TupleToMap<T>虽然很长


type DataTuple = [
  { id: 'id001'; name: 'name1' },
  { id: 'id002'; name: 'name2' },
  { id: 'id003'; name: 'name3' }
];

type TupleKeys<T> = Exclude<keyof T, keyof any[]>;
 
type TupleToObject<T extends Record<string, any>> = {
  [k in TupleKeys<T>]: { [P in T[k]['id']]: T[k]['name'] };
}[TupleKeys<T>];

type MergeUnions<T> = (T extends any ? (x: T) => any : never) extends (x: infer R) => any
  ? { [K in keyof R]: R[K] }
  : never;

type TupleToMap<T> = MergeUnions<TupleToObject<T>>;
type DataMap = TupleToMap<DataTuple>

ts playground link

对于 TypeScript 4.1+,您可以像这样实现 TupleToMap,假设您知道 id 属性 应该是键,而 name 属性 应该是值:

type TupleToMap<T extends { id: PropertyKey, name: any }[]> = {
    [V in T[number]as V['id']]: V['name']
};

type DataMap = TupleToMap<DataTuple>;
/* type DataMap = {
id001: "name1";
id002: "name2";
id003: "name3";
} */

上面使用了TS 4.1中引入的key remapping。对于早期版本,您可以这样写:

type TupleToMap<T extends { id: PropertyKey, name: any }[]> = {
    [K in T[number]['id']]: Extract<T[number], { id: K }>['name']
};

对于 MapToTuple,不清楚您打算从何处获取元组的 排序。对于人类来说,带有 id001 的条目应该出现在带有 id002 的条目之前,但对于编译器而言,我不确定是否值得尝试让其通过。如果我想出一些不疯狂的东西,我会回来的。

Playground link to code