Exploring the functionality of Object.keys() method in TypeScript can provide insights into its typing within the standard library, as seen below:
interface ObjectConstructor {
keys(o: object): string[];
}
The return type specified is string[]
. This signifies that Object.keys(obj)
yields a value of type Array<string>
, rather than Array<keyof typeof obj>
, where each key represents a union based on the known keys of an object. The compiler may not be able to ascertain if an object only contains the exact keys it knows about.
If attempting to access an object using a solely known string
key, confirmation of having a string
index signature for the object is necessary. Failure results in the compiler generating warnings and assuming the any type.
In scenarios where you are certain of an object's contained keys (e.g., a string enum
without reverse mappings), employing a type assertion informs the compiler accordingly:
const keys = Object.keys(AnEnum) as Array<keyof typeof AnEnum>;
Subsequently, iterating through the loop works as intended:
keys.forEach(key => {
console.log(AnEnum[key]); // outputs correctly
});
Note that utilizing
Object.keys(obj) as Array<keyof typeof obj>
has limitations too. If additional unknown keys exist within
obj</code}, especially with values differing from the known ones, runtime errors may occur despite successful compilation:</p>
<pre><code>interface Foo {
a: string,
b: string
}
const f = { a: "hello", b: "goodbye", c: 123 };
const foo: Foo = f;
(Object.keys(foo) as Array<keyof Foo>).forEach(k => console.log(foo[k].toUpperCase()));
// Prints:
// HELLO
// GOODBYE
// 💥 RUNTIME ERROR! foo[k].toUpperCase is not a function
It is advised to exercise caution when implementing such practices!
Check out this Playground link for interactive code snippets