Home > other >  traverse keys of enum gives typing issue
traverse keys of enum gives typing issue

Time:01-20

Suppose you want to print all values of an enum

enum AnEnum {
    a = 'a',
    b = 'b'
}

const keys = Object.keys(AnEnum);

keys.forEach(key => {  
     console.log(AnEnum[key]);
});

This gives the following error

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'typeof AnEnum'. No index signature with a parameter of type 'string' was found on type 'typeof AnEnum'

enter image description here DEMO

I was only able to fix this is with adding any

(AnEnum as any)[key]

but I guess there should be better solution. Any suggestions?

CodePudding user response:

The Object.keys() method is given a typing in the TypeScript standard library like this:

interface ObjectConstructor {
  keys(o: object): string[];
}

The return type is string[]. That means Object.keys(obj) is a value of type Array<string> and not a value of type Array<keyof typeof obj> (where the keyof operator represents a union of the known keys of an object type). See this question for a thorough explanation of why. Here I'll just say that the compiler cannot guarantee that an object has only the keys it knows about.

And that means if you try to index into an object with a key only known to be string, that will only work if that object is known to have a string index signature. If not, then the compiler will complain that it has no idea what property may or may not be a string index, and it will produce a value of the any type and a compiler warning.


In cases like this where you are sure that your object only has the known keys (e.g., a string enum where there's no reverse mapping), you can use a type assertion to tell the compiler that:

const keys = Object.keys(AnEnum) as Array<keyof typeof AnEnum>;

And then your loop will work:

keys.forEach(key => {
    console.log(AnEnum[key]); // okay
});

Do note that Object.keys(obj) as Array<keyof typeof obj> is not a panacea. If it turns out that obj does have more keys than the compiler knows about, and worse, the property values at these keys are not the same type as the values at the known keys, then you can write something that compiles fine but explodes at runtime:

interface Foo {
    a: string,
    b: string
}
const f = { a: "hello", b: "goodbye", c: 123 };
const foo: Foo = f; // okay, f is a valid Foo

(Object.keys(foo) as Array<keyof Foo>).forEach(k => console.log(foo[k].toUpperCase()));
// HELLO
// GOODBYE
//            
  •  Tags:  
  • Related