Home > front end >  Map a tuple of objects to an object in typescript types
Map a tuple of objects to an object in typescript types

Time:10-31

I have a type that looks like this:

type Config = {
  endpoints: [
    {name: "abc", created: false},
    {name: "xyz", created: true},
  ]
}

I have a function that transforms this type into the following:

type Instance = {
  abc: {name: "abc", created: false},
  xyz: {name: "xyz", created: false},
}

I wrote the following transformation type

type IntersectOf<U extends any> = (U extends unknown ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;

type Transform<T extends Config, K extends (keyof T)[] = (keyof T)[]> = IntersectOf<{
  [key in keyof K]:
    K[key] extends keyof T['endpoints']
    // here it says "name" can't be used as index of `T["endpoints"][K[key]]`
    ? Record<T['endpoints'][K[key]]['name'], T['endpoints'][K[key]]>
    : never;
}[number]>;

type Instance = Transform<Config>;

I also tried adding a number type guard, and the error goes away but Instance becomes unknown. Here is the Transform type with number typeguard:

type Trasnform<T extends Config, K extends (keyof T)[] = (keyof T)[]> = IntersectOf<{
  [key in keyof K]:
    K[key] extends keyof T['endpoints']
    ? K[key] extends number
    ? Record<T['endpoints'][K[key]]['name'], T['endpoints'][K[key]]>
    : never
    : never;
}[number]>;

CodePudding user response:

My suggestion would be to write a key-remapping mapped type that acts on the union of element types of the endpoints property, like this:

type Transform<T extends { name: string }> =
    { [U in T as U['name']]: U }

And then you pass in, not Config, but the indexed access type Config['endpoints'][number] corresponding to the element types:

type Instance = Transform<Config['endpoints'][number]>;

That produces this:

/* type Instance = {
    abc: {
        name: "abc";
        created: false;
    };
    xyz: {
        name: "xyz";
        created: true;
    };
} */

which is what you were looking for.

Playground link to code

CodePudding user response:

A proposal (playground):

export type IndicesOf<T> = Exclude<keyof T, keyof any[]>;

type Instance = { 
  [Idx in IndicesOf<Config["endpoints"]> 
    as Config["endpoints"][Idx]["name"]]: Config["endpoints"][Idx]
}
  • Related