Repository: TypeScript
Issue: Allow iterating through enum variants with for of
Labels: Suggestion,Awaiting More Feedback

# Suggestion

## 🔍 Search Terms

enum, iterate, variants

## ✅ Viability Checklist

My suggestion meets these guidelines:

* [x] This wouldn't be a breaking change in existing TypeScript/JavaScript code
* [x] This wouldn't change the runtime behavior of existing JavaScript code
* [x] This could be implemented without emitting different JS based on the types of the expressions
* [x] This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
* [x] This feature would agree with the rest of [TypeScript's Design Goals](https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals).

Note that "is this a runtime feature?" doesn't really apply in this case since enums are already a runtime feature (maybe the only one?). This suggestion just modifies the compilation output slightly.

## ⭐ Suggestion

The Typescript code for an enum...

```ts
enum Colour {
  Red,
  Green,
  Blue,
}
```

...generates Javascript like this:

```js
var Colour;
(function (Colour) {
    Colour[Colour["Red"] = 0] = "Red";
    Colour[Colour["Green"] = 1] = "Green";
    Colour[Colour["Blue"] = 2] = "Blue";
})(Colour || (Colour = {}));
```

Unfortunately there's no nice way to iterate over the enum values. The prevailing suggestion on [these two](https://stackoverflow.com/questions/18111657/how-to-get-names-of-enum-entries) very [highly upvoted StackOverflow questions](https://stackoverflow.com/questions/39372804/how-can-i-loop-through-enum-values-for-display-in-radio-buttons) is to use `for in` and then filter out the reverse mapping entries using `typeof`. That sucks:

* It's very hacky. Probably really buggy.
* The types are wrong - you get `string` or `number` instead of `Colour`.
* It can't handle duplicated enum variants, e.g. `enum { Zero = 0, None = 0, ...}`, although I'm not 100% sure how duplicates should be handled anyway.

I propose that the above enum code should compile to something like this:

```js
var Colour;
(function (Colour) {
    Colour[Colour["Red"] = 0] = "Red";
    Colour[Colour["Green"] = 1] = "Green";
    Colour[Colour["Blue"] = 2] = "Blue";
    Colour[Symbol.iterator] = function* () {
      yield 0;
      yield 1;
      yield 2;
    };
})(Colour || (Colour = {}));
```

This would allow you to write:

```
function paintColour(c: Colour) {...}

for (const c of Colour) {
  paintColour(c);
}
```

## 📃 Motivating Example / Use Cases

Probably the most common use is generating UI from enums (e.g. a colour drop-down), but this is very general purpose. The StackOverflow questions linked above demonstrate how much people want this. We've come across this twice already in our code - once for UI and once for collecting data per enum variant, e.g.:

```ts
enum Category {
  Code,
  Data,
  Constant,
  Variable,
  Temporary,
  ... lots more ...
}

const totals: Map<Category, number> = new Map();

// Add a 0 entry for all entries.
for (const cat of Category) {
  totals.set(cat, 0);
}

for (const variable of ...) {
  totals.set(totals.get(variable.category) + variable.size);
}
```

<!--(Please don't say "but you can check if the map key exists and use totals.entries()"; no you can't.)-->

Note there is a related but slightly different bug here: #39627 but it's more about how `for in` does unexpected things than about making `for of` work. I don't think it really matters since you should never ever use `for in` anyway.