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 CarType
s, 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.
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
}