All Articles

How to iterate over objects in TypeScript

From Effective TypeScript: 62 Specific Ways to Improve Your TypeScript by Dan Vanderkam

It is an excellent book. Please purchase the book to support its author!

If you are the publisher and think this article should not be public, please write me an email to trungk18 [at] gmail [dot] com and I will make it private.

This code runs fine, and yet TypeScript flags an error in it. Why?

How to iterate over objects in TypeScript

Inspect the obj and k symbols give a clue:

How to iterate over objects in TypeScript

How to iterate over objects in TypeScript

The type of k is a string, but you’re trying to index into an object whose type only has three specific keys: 'one', 'two', and 'three'. There are strings other than these three, so this has to fail. Plugging in a narrower type declaration for k - let k: keyof typeof obj fixes the issue:

How to iterate over objects in TypeScript

To understand, let’s look at a slightly different example involving an interface and a function:

interface ABC {
  a: string
  b: string
  c: number
}

function foo(abc: ABC) {
  for (const k in abc) {
    // const k: string
    const v = abc[k]
    // ~~~~~~ Element implicitly has an 'any' type
    //        because type 'ABC' has no index signature
  }
}

It’s the same error as before. And you can “fix” it using the same sort of declaration (let k: keyof typeof abc). But in this case, TypeScript is right to complain. Here’s why:

How to iterate over objects in TypeScript

The function foo can be called with any value assignable to ABC, not just a value with "a", "b" and "c" properties. It’s entirely possible that the value will have other properties, too. To allow for this, TypeScript gives k the only type it can be confident of, namely string.

Using the keyof declaration would have another downside here:

How to iterate over objects in TypeScript

If "a" | "b" | "c" is too narrow for k, then string | number is certainly too narrow for v. In the preceding example, one of the values is a Date, but it could be anything. The types here give a false sense of certainty that could lead to chaos at runtime.

So what if you just want to iterate over the object’s keys and values without type error?

Object.entries lets you iterate over both simultaneously

interface ABC {
  a: string
  b: string
  c: number
}

function foo(abc: ABC) {
  for (const [k, v] of Object.entries(abc)) {
    k // Type is string
    v // Type is any
  }
}

While these types may be hard to work with, they are at least honest!

You should also be aware of the possibility of prototype pollution. Even in the case of an object literal that you define, for-in can produce additional keys:

Object.prototype.z = 3 //Please don't do this!
const obj = { x: 1, y: 2 }
for (const k in obj) {
  console.log(k)
}
//Print x y z

Hopefully, this doesn’t happen in a real environment (You should never add enumerable properties to Object.prototype), but it is another reason that for-in produces string keys even for object literals.

If you want to iterate over the keys and value in an object, use either:

  • a keyof declaration (let k: keyof T)
  • Object.entries

The former is appropriate for constants or other situations where you know that the object won’t have additional keys, and you want precise types. The latter is more generally appropriate, though the key and value types are more challenging to work with.

Things to remember

  • Use let k: keyof T and a for-in loop to iterate objects when you know exactly what the keys will be. Be aware that any objects your function receives as parameters might have additional keys.
  • Use Object.entries to iterate over the keys and values of any object.
Published 17 Oct 2020

Recent Posts

How to copy an object from the Chrome inspector console as code

Have you ever do console.log an object and wondering how can copy the object over?

Design and build a scalable menu with Angular and ng-zorro


Follow @tuantrungvo on Twitter for more!