4

I have a string enum in TypeScript, and I want to iterate over it like below. However, when I do this the type of the iterator is a string instead of the enum type.

enum Enum { A = 'a', B = 'b' };

let cipher: { [key in Enum]: string };

for (const letter in Enum) {
    cipher[letter] = 'test'; // error: letter is of type 'string' but needs to be 'Enum'
}

The exact error I get is this:

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ a: string; b: string; }'.
  No index signature with a parameter of type 'string' was found on type '{ a: string; b: string; }'.ts(7053)

This seems strange as it's guaranteed the letter is an Enum. Is there any way to work around this?

3
  • 2
    I don't think my explanation is perfect so I'm posting this as a comment. cipher will have the keys as the Enum values, that is { a: string, b: string } because you are using a string enum, but when you are iterating with the for ... in you are iterating over the enumerable props of the generated object that is ['A', 'B'] because the object is generated as var Enum; (function (Enum) { Enum["A"] = "a"; Enum["B"] = "b"; })(Enum || (Enum = {})); Commented Apr 9, 2021 at 23:25
  • 1
    so A workaround would be to iterate over the values of the object with the for ... of and Object.values because the Enum is not directly iterable. final code: for (const letter of Object.values(Enum)) { cipher[letter] = 'test'; // error: letter is of type 'string' but needs to be 'Enum' } Commented Apr 9, 2021 at 23:26
  • @robertgr991 that works perfectly, thanks! Commented Apr 10, 2021 at 0:25

1 Answer 1

4

I'll post my comments as an answer so the questions won't remain unanswered.

The type of cipher will have the keys as the values of Enum, that is:

let cipher: {
  a: string;
  b: string;
}

because you are using a string enum. But when iterating with for...in you iterate over the enumerable properties of the generated object, those properties are ['A', 'B'] because the generated object(TypeScript v4) would be:

var Enum;
(function (Enum) {
    Enum["A"] = "a";
    Enum["B"] = "b";
})(Enum || (Enum = {}));

So you need to iterate over the enum values. For this you can use Object.values to get an array of it's values and for...of to iterate over it. This way, letter type will be Enum.

for (const letter of Object.values(Enum)) {
    cipher[letter] = 'test'; // error: letter is of type 'string' but needs to be 'Enum'
}

I never used for...in with an enum before but I would have expected the compiler to have enough information so the for...in strictly types letter to a union of "A" | "B" but it seems that it widens the type to string.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.