ObjectEntries
Challenge
Implement the type version of Object.entries
. For example:
interface Model {
name: string;
age: number;
locations: string[] | null;
}
type modelEntries = ObjectEntries<Model>;
// ['name', string] | ['age', number] | ['locations', string[] | null];
Solution
Looking at the problem, the idea is to use
Mapped Types
to iterate over each key in the object and generate [key, typeof key]
for each
key.
type ObjectEntries<T> = { [P in keyof T]: [P, T[P]] };
// { key: [key, typeof key], ... }
Finally, to convert the generated type as a union, we’ll just append keyof T
at the end.
type ObjectEntries<T> = { [P in keyof T]: [P, T[P]] }[keyof T];
// [key, typeof key] | ...
So how does appending keyof T
generates union you ask? Well, say you had an
object:
const obj = {
foo: "bar",
};
In order to access the foo
property, we could use a dot operator(obj.foo
) or
a square bracket syntax(obj["foo"]
).
In our solution above, we are using the same trick. Since keyof T
can be any
of the keys present in T
, TypeScript generates all possible outcomes and turns
them into a union type.
Coming back to our problem, notice how some of the test cases have passed. Taking a closer look at the test cases which have failed, we find that:
- All of the optional keys have to be converted into required. We can do so by using “Mapping Modifier”.
- Since the
Partial
type appends undefined to a type, we have to take care of that.
type HandleUndefined<F, S extends keyof F> = F[S] extends infer R | undefined
? R
: F[S];
type ObjectEntries<T> = {
[P in keyof T]-?: [P, HandleUndefined<T, P>];
}[keyof T];
Comments