Why does Object.keys on a Record return string[]?
--
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…