Why does Object.keys on a Record return string[]?

Charles Stover
5 min readDec 13, 2021

Intuitively, one may expect Object.keys on Record<K, T> to return K[], but it instead returns string[]. Why?

What is a record?

A record of type Keys to Type is an object whose property keys of type Keys are mapped to property values of type Type. For example, a Record<string, number> is an object where any string property will return a number value. For example, obj.someString may return the number 123, because we are accessing a string property of obj: someString.

It may be intuitive to read Record<string, number> like maps, as “a record of strings to numbers.” That is, for any string, this record associates a number to it.

How do you use a record?

Records are particularly interesting and useful when you are using something more finite than primitives.

enum Friend {
Ace = 'ace',
Charles = 'charles',
Eric = 'eric',
}
interface Profile {
readonly height: number;
}
const profiles: Record<Friend, Profile> = {
[Friend.Ace]: {
height: 64,
},
[Friend.Charles]: {
height: 70,
},
[Friend.Eric]: {
height: 68,
},
};

In the above example, the profiles record is a record of friends to profiles. That is, if I were to look up a friend, I will receive a profile. profiles.Charles returns Charles’s profile.

If I were to add a name to the friends enum, Friend.Elizabeth, TypeScript will fail to transpile. I declared profiles to be a record of friends to profiles but there is no Friend.Elizabeth entry. This is great type checking by TypeScript, and exactly why we use it. I told TypeScript that this record should be an entry for every friend, and oversights like this don’t get delivered to customers in production.

By adding an entry: [Friend.Elizabeth]: { height: 66 }, TypeScript will transpile successfully, and the application will work as expected: displaying all friends and their height.

What are the properties of a record?

Here’s the nitty gritty. If you wanted all the keys of a record, you may expect to receive the keys in its definition. It’s intuitive to think that Object.keys on a Record<Friend, Profile> would return Friend[]. In the above example, Object.keys(profiles) absolutely returns ['ace', 'charles', 'elizabeth', 'eric'] — and…

--

--