Frontend Development 6 min read

Why Object.keys Fails in TypeScript and How to Fix It

This article explains why iterating over object keys with Object.keys in TypeScript often yields unexpected results, analyzes the underlying type‑system reasons, and presents four practical solutions—including type assertions, type predicates, generic functions, and wrapper utilities—to reliably access object properties.

Code Mala Tang
Code Mala Tang
Code Mala Tang
Why Object.keys Fails in TypeScript and How to Fix It

Iterating over object keys with Object.keys in TypeScript can be frustrating because the method returns a string[] rather than a union of the object's actual keys, which is intentional in the language.

The article presents several ways to work around this limitation.

Root Cause Analysis

Object.keys returns a string array, not a union of the object's keys, so TypeScript cannot guarantee the keys exist on the object.

Common fix: cast the result to Array<keyof typeof obj> or use inline type narrowing.

<code>const user = {
  name: "Daniel",
  age: 26,
};
const keys = Object.keys(user) as Array<keyof typeof user>;
keys.forEach(key => {
  console.log(user[key]);
});
</code>

Another approach is to narrow the type inline.

<code>function isKey<T extends object>(x: T, k: PropertyKey): k is keyof T {
  return k in x;
}
const keys = Object.keys(user);
keys.forEach(key => {
  if (isKey(user, key)) {
    console.log(user[key]);
  }
});
</code>

These techniques address the fact that Object.keys does not return the exact key types.

Solutions

Solution 1: Cast to keyof typeof

Convert the result of Object.keys to an array containing the specific keys.

<code>const keys = Object.keys(user) as Array<keyof typeof user>;
keys.forEach(key => console.log(user[key]));
</code>

You can also cast each key when accessing the property.

<code>const keys = Object.keys(user);
keys.forEach(key => console.log(user[key as keyof typeof user]));
</code>

Using as is unsafe because it bypasses compile‑time checks.

<code>const nonExistentKey = "id" as keyof typeof user;
const value = user[nonExistentKey]; // No error at compile time
</code>

Solution 2: Type Predicates

Define a type‑predicate function to verify a key exists before indexing.

<code>function isKey<T extends object>(x: T, k: PropertyKey): k is keyof T {
  return k in x;
}
const keys = Object.keys(user);
keys.forEach(key => {
  if (isKey(user, key)) {
    console.log(user[key]);
  }
});
</code>

Solution 3: Generic Function

Use a generic function with a for...in loop, which narrows the key type.

<code>function printEachKey<T extends object>(obj: T) {
  for (const key in obj) {
    console.log(obj[key]);
  }
}
printEachKey({ name: "Daniel", age: 26 });
</code>

Solution 4: Wrap Object.keys in a Function

Encapsulate the cast in a reusable helper.

<code>const objectKeys = <T extends object>(obj: T) => Object.keys(obj) as Array<keyof T>;

const keys = objectKeys({ name: "Daniel", age: 26 });
console.log(keys);
</code>

Conclusion

The author prefers the first solution because it is the most readable and intuitive.

TypeScripttype safetygeneric functionObject.keyskeyoftype predicates
Code Mala Tang
Written by

Code Mala Tang

Read source code together, write articles together, and enjoy spicy hot pot together.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.