Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Type-safe map of objects with corresponding names

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:

  1. The name parameter will be used as an exact string literal for the attribute’s name.
    For instance, Attribute<"width", number> instead of Attribute<string, number>.
  2. The real scenario the Attribute type will have much more properties, but for simplicity it has only value.

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.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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
}

playground

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading