In theory, you can then use those keys to access the values of the object:
keys.forEach((key) => {
console.log(yetiSeason[key]); // Red squiggly line under key
});
// Hovering over key shows:
// Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ title: string; artist: string; releaseYear: number; }'.But we're getting an error. TypeScript is telling us that we can't use string to access the properties of yetiSeason.
The only way this would work would be if key was typed as 'title' | 'artist' | 'releaseYear'. In other words, as keyof typeof yetiSeason. But it's not—it's typed as string.
The reason for this comes back to Object.keys—it returns string[], not (keyof typeof obj)[].
const keys = Object.keys(yetiSeason); // string[]By the way, the same behavior happens with for ... in loops:
for (const key in yetiSeason) {
console.log(yetiSeason[key]); // Red squiggly line under key
}This is a consequence of TypeScript's open object types. TypeScript can't know the exact keys of an object at compile time, so it has to assume that there are unspecified keys on every object.
It's possible to work around this, by using keyof typeof obj to get the keys of an object.
But it's not always advisable. TypeScript's caution protects us from a nasty error:
const yetiSeason = {
title: "Yeti Season",
artist: "Yeti",
releaseYear: 2024,
};
type Album = {
title: string;
artist: string;
};
const staleAlbum: Album = yetiSeason;
for (const key in staleAlbum) {
// No error at compile time,
// but errors at runtime with:
// "staleAlbum[key].toLocaleLowerCase is not a function"
console.log(staleAlbum[key as keyof Album].toLocaleLowerCase());
}The safest thing for it to do is, when you're enumerating the keys of an object, to treat them all as string. However, you'll find a few ways to work around this in this chapter's exercises.