Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript Key-Value relation preserving Object.entries type

Tags:

The typing for Object.entries provided by typescript has the return type [string, T][] but I am searching for a generic type Entries<O> to represent the return value of this function that keeps the relationship between the keys and the values.

Eg. when having an object type like

type Obj = {     a: number,     b: string,     c: number } 

I'm looking for a type Entries<O> that results in one of the types below (or something similar) when provided with Obj:

(["a", number] | ["b", string] | ["c", number])[] [["a", number], ["b", string], ["c", number]] (["a" | "c", number] | ["b", string])[] 

That this isn't correct for all use cases of Object.entries (see here) is no problem for my specific case.


Tried and failed solution:

type Entries<O> = [keyof O, O[keyof O]][] doesn't work for this as it only preserves the possible keys and values but not the relationship between these as Entries<Obj> is ["a" | "b" | "c", number | string].

type Entry<O, K extends keyof O> = [K, O[K]] type Entries<O> = Entry<O, keyof O>[] 

Here the definition of Entry works as expected eg. Entry<Obj, "a"> is ["a", number] but the application of it in the second line with keyof O as the second type variable leads again to the same result as the first try.

like image 628
Malte Laukötter Avatar asked Feb 09 '20 22:02

Malte Laukötter


People also ask

How do I store a key value pair in TypeScript?

Use Typescript Map for Storing the Key-Value pair. Calling the object with the key will return the value for that particular pair. We can also store multiple values in a single key. The insertion order is preserved in the Typescript Map Key value.

What is key-value object?

The keys of an object is the list of property names. The values of an object is the list of property values. The entries of an object is the list of pairs of property names and corresponding values.

How to get an object's key by its value in typescript?

Use the find () method to get the key by its value. Both of the examples in the code snippet get an object's key by value. We used the Object.keys method in the first example. The method returns an array of the object's keys. However, note that TypeScript types the return value of the Object.keys () method as string [].

What is a key-value pair in typescript?

Typescript comes up with functions that can be used for writing scripts. A key-value pair is a wonderful functionality in an object-oriented programming approach that can be used in Typescript for generating values. These key-value pairs in Typescript are present in the Typescript Object. The values can be function, an array of objects, etc.

What is type assertion in typescript?

TypeScript is telling us that we can't index the object with any string key, it has to be name, department or country. This is why we used a type assertion to type the return value of the Object.keys () method. Now the key parameter in the find method is a union type of the objects keys, so everything works as expected.

How to iterate over an objet in typescript?

If we wanted to iterate over that objet properties in JavaScript we could simply do the following: Unfortunately, in TypeScript that would give the following error. obj [property]: Element implicitly has an ‘any’ type because expression of type ‘string’ can’t be used to index type ‘ { foo: string; bar: string; }’.


Video Answer


1 Answers

Here's a solution, but beware when using this as a return type for Object.entries; it is not always safe to do that (see below).


When you want to pair each key with something dependent on that key's type, use a mapped type:

type Entries<T> = {     [K in keyof T]: [K, T[K]]; }[keyof T][];  type Test = Entries<Obj>; // (["a", number] | ["b", string] | ["c", number])[] 

The second version, which has a tuple type containing the properties instead of a union, is much harder to construct; it is possible to convert a union to a tuple but you basically shouldn't do it.

The third version is manageable, but a bit more complicated than the first version: you need PickByValue from this answer.

type Entries3<T> = {     [K in keyof T]: [keyof PickByValue<T, T[K]>, T[K]] }[keyof T][];  type Test3 = Entries3<Obj>; // (["a" | "c", number] | ["b", string])[] 

Playground Link


I guess I should also explain why Typescript doesn't give a stronger type to Object.entries. When you have a type like type Obj = {a: number, b: string, c: number}, it's only guaranteed that a value has those properties; it is not guaranteed that the value does not also have other properties. For example, the value {a: 1, b: 'foo', c: 2, d: false} is assignable to the type Obj (excess property checking for object literals aside).

In this case Object.entries would return an array containing the element ['d', false]. The type Entries<Obj> says this cannot happen, but in fact it can happen; so Entries<T> is not a sound return type for Object.entries in general. You should only use the above solution with Object.entries when you yourself know that the values will have no excess properties; Typescript won't check this for you.

like image 183
kaya3 Avatar answered Oct 09 '22 00:10

kaya3