Typescript Key-Value relation preserving Object.entries type
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.