TypeScript

How to Iterate Over Objects in TypeScript

Picture of me, Omari
Omari Thompson-Edwards
Tue Jan 30 2024 6 min

By default, objects in TypeScript aren't iterable. Sometimes you will need to loop through objects, however, and thankfully TypeScript includes some methods to transform your object into something that you can iterate through. In this article we'll discuss options for doing so, advantages, disadvantages, and things to watch out for.

Object Methods.

There are three object methods that are very useful for iterating through objects, each providing you access to different parts of the object. They all follow the same format of a static method on the Object class which accept an object as a parameter, and return an array of results.

Iterating through Object Keys

When it comes to iterating through object keys in TypeScript, you can use the "Object.keys()" method. This method returns an array containing all enumerable property names of the given object. Let's dive into an example:

const myObject = {
    name: 'Alice',
    age: 25,
    city: 'London'
};

const keys = Object.keys(myObject);
keys.forEach(key => {
    console.log(key);
}); //name age city

Object.keys gives back an array containing all of the keys of our object. You can then iterate through this array with whichever method you choose, such as ".forEach", or mapping over the keys.

Iterating through Object Values

If you just need the values of an object, then you can use "Object.values()":

const myObject = {
    name: 'Bob',
    age: 35,
    city: 'Paris'
};

const values = Object.values(myObject);
values.forEach(value => {
    console.log(value);
}); //Bob, 35, Paris

Iterating Through Object Entries

"Object.entries()" combines the best of both worlds, and is probably what you're looking for if you're looking to iterate through an object in TypeScript. This method returns an array containing arrays of key-value pairs for each enumerable property of the provided object. Here's what it looks like:

const myObject = {
    name: 'Charlie',
    age: 40,
    city: 'Berlin'
};

const entries = Object.entries(myObject);
entries.forEach(([key, value]) => {
    console.log(`${key}: ${value}`);
});

For...In.

One of the fundamental methods for iterating through objects in TypeScript is the "for...in" loop. Here's what it looks like:

const myObject = {
    name: 'John',
    age: 30,
    city: 'New York'
};

for (const key in myObject) {
    console.log(key);
}

 It iterates through the keys of an object, meaning if you want access to the values. The Object methods were introduced in ES6, before that this was the way to iterate through objects. Another thing to note is that this method will go down the prototype chain, a mechanism that allows objects to inherit properties and methods from other objects.

We can see that in this example:

const myObject = {
    foo: 'bar',
    __proto__: {
        baz: 'qux',
    },
};

console.log('Object.keys:', Object.keys(myObject));
console.log('For...in:');
for (const key in myObject) {
    console.log(key);
}

Which outputs:

Code_FdSCvH1RtF.png

For...Of.

"For...in" has a very similar counterpart in the "for...of" loop. While "for...in" is specifically for object keys, "for...of" iterates through any iterable objects, e.g. arrays, strings, and in our case, maps.

If you're looking to iterate through dictionaries or maps in TypeScript, the "for...of" loop is the easiest way to do so. Here's what that looks like:

const myObject = {
    name: 'John',
    age: 30,
    city: 'New York',
};

const myMap = new Map(Object.entries(myObject));

for (const [key, value] of myMap) {
    console.log(`${key}: ${value}`);
}

Typing Keys.

Something important to note is the type that TypeScript will give your keys. If we take a look at this code:

const myObject = {
    name: 'Charlie',
    age: 40,
};
const keys = Object.keys(myObject);
//const keys: string[]

The type might not be as specific as you'd expect - instead of the keys of our object, we get just "string[]".

So using those keys to access properties in the object might not work as you expect:

console.log(keys.map((k) => myObject[k])); //Element implicitly has an 'any' type because expression of type 'string' can't be used to index type
for (const key in myObject) {
    //Element implicitly has an 'any' type because expression of type 'string' can't be used to index type
    console.log(myObject[key]);
}

How do we fix this? You need to check before if the key is in the object - here's a handy helper function to do so:

function isKey<T extends object>(obj: T, k: PropertyKey): k is keyof T {
    return k in obj;
}

This uses a type guard - aka a function that tells TypeScript if a variable is of a type. in our case if the key provided is more specifically a key of our object.

Now we can fix our "for...in" loop:

for (const key in myObject) {
    if (isKey(myObject, key)) console.log(myObject[key]);
}

And fix our map:

console.log(keys.map((k) => (isKey(myObject, k) ? myObject[k] : null)));

You might be wondering why this is the case in the first place, after all why would everything in "Object.keys" not just be the keys of our object?

The key lies in the fact that TypeScript types are not runtime types - they don't always represent only the properties an object can have. Take a look at this example. If we have these two types:

type NamedItem = {
    name: string;
};
type ShopItem = {
    name: string;
    quantity: number;
};

We can see that NamedItem is a subtype of ShopItem. This means that wherever you need a NamedItem, a ShopItem will also be accepted, e.g. in this function:

function printItemKeys(item: NamedItem) {
    console.log(Object.keys(item));
}
const shopItem: ShopItem = { name: 'Apple', quantity: 8 };

printItemKeys(shopItem); //👍

In this example if "Object.keys" did have a strict type of just the keys in "NamedItem" then it would be wrong. Instead, TypeScript assumes a more general type.

Symbols.

None of these methods will include non-enumerable keys.  This means they don't include symbols. If you're not familiar, symbols in JavaScript are unique and immutable primitives.

const hidden = Symbol('hidden');
const myObject = {
    name: 'Charlie',
    age: 40,
    [hidden]: 'Hidden Key',
};
console.log(Object.keys(myObject));
for (const key in myObject) {
    console.log(key);
}

If you do want to iterate through the symbols of an object, there is a built in method:

console.log(Object.getOwnPropertySymbols(myObject));

The slightly long-winded "Object.getOwnPropertySymbols()" returns a list of all of the symbols in an object for you to iterate through.

Conclusion.

So which one should you use? For plain objects, I would reach for one of the Object methods, picking whichever one you need depending on if you need just the keys, just the values, or both. Just be aware if you're using anything non-enumerable like symbols. For Maps, "for...of" is especially useful. Thanks for reading - if you liked this article, feel free to share it!

read more.

Me
Hey, I'm Omari 👋

I'm a full-stack developer from the UK. I'm currently looking for graduate and freelance software engineering roles, so if you liked this article, feel free to reach out.