Home > Enterprise >  Mapping array of union type in object
Mapping array of union type in object

Time:06-27

I have the following example that I'd like to make it work. See comments in code:

const cars = {
    "audi": {
        manufacturer: "audi",
        colors: ["red", "blue"]
    },
    "mercedes": {
        manufacturer: "mercedes",
        colors: ["blue", "black"]
    }
} as const

type CarType = typeof cars[keyof typeof cars]

type Mapper<C extends CarType> = {
    manufacturer: C["manufacturer"]
    color: C["colors"][number]
}

type Car = Mapper<CarType>

const car: Car = {
    manufacturer: "audi",
    color: "black" // <- this is wrong, only "red" and "blue" are valid
}

As it can be seen from the example, I would like to have a color field that has only a single option and is derived from the list of valid colors.

The issue is that my Mapper type is wrong, because it does not pick out individual CarTypes, but I don't know how to write it.

CodePudding user response:

A stand-alone type that achieves this would look like this:

type Car = {
  [K in keyof typeof cars]: {
    manufacturer: typeof cars[K]["manufacturer"],
    colors: typeof cars[K]["colors"][number]
  }
}[keyof typeof cars]

You can map over the cars to create a valid object for each key of cars. With [keyof typeof cars] we can index this type to get a union of all possible car types.

Playground


A more generic solution:

type Mapper<T extends Record<string, { manufacturer: string, colors: readonly string[] }>> = {
  [K in keyof T]: {
    manufacturer: T[K]["manufacturer"],
    colors: T[K]["colors"][number]
  }
}[keyof T]

const car: Mapper<typeof cars> = {
    manufacturer: "audi",
    color: "black" // <- this is wrong, only "red" and "blue" are valid
}
  • Related