Let’s say I have a generic type with the name
property:
type Attribute<Name, Value> = {
name : Name
value : Value
}
The important parts here are that:
- The
name
parameter will be used as an exact string literal for the attribute’s name.
For instance,Attribute<"width", number>
instead ofAttribute<string, number>
. - The real scenario the
Attribute
type will have much more properties, but for simplicity it has onlyvalue
.
Now I want to have a map (object, dictionary, {}
) which can map Attribute["name"]
to the corresponding Attribute
.
To put it simple, the map m
of types Attribute<"width", number>
and Attribute<"height", number>
should return Attribute<"width", number>
on accessing m.width
and Attribute<"height", number>
on m.height
.
Like this:
function f(m : AttributesMap<[
Attribute<"width", number>,
Attribute<"height", number>,
]>) {
m.width.name // "width" type
m.height.name // "height" type
}
To achieve this I tried the following approach:
type AttributesMap<
Attribute_ extends Attribute<string, any>[]
> = {
[key in Attribute_[number][`name`]] : Attribute_[number]
}
But it returns Attribute<"width", number> | Attribute<"height", number>
on accessing both m.width
and m.height
.
I understand that it does so because of the [number]
property at the left is totally independent of the same [number]
on the right.
But I cannot figure out how to make it work properly.
Any ideas?
>Solution :
You get a union because in the values you are accessing every value by using indexed access:
Attribute_[number]
Instead of this, let’s map through Attribute_[number]
. Then we can map through the union using mapped type and change the key to name
property by using the key remapping:
type AttributesMap<Attribute_ extends Attribute<string, any>[]> = {
[K in Attribute_[number] as K['name']]: K;
};
Testing:
function f(
m: AttributesMap<[Attribute<'width', number>, Attribute<'height', number>]>,
) {
m.width.name; // "width" type
m.height.name; // "height" type
}