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
nameparameter 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
Attributetype 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
}