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

问题描述

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

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',....
}; 
type DataTuple = MapToTuple<DataMap>;

// or 

type DataTuple = [
  {id: 'id001',...
];
type DataMap = TupletoMap<DataTuple>;

这些函数 MapToTuple<T>TupletoMap<T> 是否可能?

解决方法

对于 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

,

我有 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