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

872 views Asked by At

I need the same data served in these two types

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

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

I would like to only declare this type once and have a single source of truth, But I cannot figure out a utility type function that translates the type from an object to a tuple.

ideally I would like to do something like this:

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>;

Are either of these functions MapToTuple<T> or TupleToMap<T> possible?

2

There are 2 answers

3
jcalz On BEST ANSWER

For TypeScript 4.1+ you can implement TupleToMap like this, assuming you know that the id property should be the key and the name property should be the value:

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";
} */

The above uses key remapping which was introduced in TS 4.1. For earlier versions you could write it this way instead:

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

For MapToTuple it's not clear where you intend to get the ordering of the tuple from. It might be obvious to a human being that the entry with id001 should come before the entry with id002, but for the compiler I'm not sure if it's worth trying to get that across. I will circle back if I come up with something not crazy.

Playground link to code

0
lonewarrior556 On

I have a solution for TupleToMap<T> Although it is pretty long


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