0

enum Direction {
    Down = -1,
    Up = 1
}

let i = 1;
for (const direction of Object.values(Direction)) {
    console.log('iteration ' + i++);
    console.log(direction);
}

iteration 1

Up

iteration 2

-1

iteration 3

Down

iteration 4

1

I was expecting:

iteration 1

-1

iteration 2

1

Generated js:

var Direction;
(function (Direction) {
    Direction[Direction["Down"] = -1] = "Down";
    Direction[Direction["Up"] = 1] = "Up";
})(Direction || (Direction = {}));
var i = 1;
for (var _i = 0, _a = Object.values(Direction); _i < _a.length; _i++) {
    var direction = _a[_i];
    console.log('iteration ' + i++);
    console.log(direction);
}

I'm sure there's a workaround but I really just want to know what causes this to go against every single expectation I had

Moritz
  • 11
  • 3
  • 3
    Have you checked the generated JS yet? – kelsny Apr 11 '23 at 14:54
  • [How to get names of enum entries?](https://stackoverflow.com/questions/18111657/how-to-get-names-of-enum-entries) and https://stackoverflow.com/questions/39372804/how-can-i-loop-through-enum-values-for-display-in-radio-buttons – kelsny Apr 11 '23 at 15:04
  • TS enums aren't really meant to be iterated (or, from my experience, they really aren't meant for most of the things people try to do with them); you're running into [reverse mappings](https://www.typescriptlang.org/docs/handbook/enums.html#reverse-mappings), which are maybe helpful to some people, but apparently trip people up. Does that fully address the question? If so I'll write an answer; if not, what am I missing? – jcalz Apr 11 '23 at 15:04
  • Yeah that sounds like what I'm looking for – Moritz Apr 11 '23 at 15:06
  • @pink as I said I don't need a solution, just an explanation, which I guess is pretty much just you're not supposed to iterate over enums. – Moritz Apr 11 '23 at 15:12
  • lol I was gonna ad an answer but got rate limited..? Anyways I was gonna link to [my solution](https://stackoverflow.com/questions/73119114/how-to-iterate-through-enums-with-integer-as-value-in-typescript/75987379#75987379) to get it to work. – Moritz Apr 11 '23 at 15:15
  • The way I normally suppress reverse mappings is to check that the key is not a numeric-like string (since it needs to be a valid identifier to be a forward mapping), and that way mixed string-numeric enums are still usable – jcalz Apr 11 '23 at 15:32

1 Answers1

1

Just take a look at the generated JavaScript output:

var Direction;
(function (Direction) {
    Direction[Direction["Down"] = -1] = "Down";
    Direction[Direction["Up"] = 1] = "Up";
})(Direction || (Direction = {}));

We're only going to focus on the two lines inside:

Direction[Direction["Down"] = -1] = "Down";
Direction[Direction["Up"] = 1] = "Up";

Assignments are really just expressions that "return" the new value assigned to the target. So, after we assign -1 to Down and 1 to Up, we assign "Down" to -1 and "Up" to 1, which gives us a reverse mapping that allows you to look up the name of an enum member:

console.log(Direction[Direction.Down]); // "Down"

Basically, your enum looks like this at runtime:

{
  "1": "Up",
  "Down": -1,
  "-1": "Down",
  "Up": 1
}

However, this behavior is only seen in numeric enums. For example, a string enum like this will not produce a reverse mapping:

enum Direction {
    Down = "down",
    Up = "up",
}

As for the fix, you can check if the value is a number or not:

let i = 1;
for (const direction of Object.values(Direction)) {
    if (typeof direction !== "number") continue;

    console.log('iteration ' + i++);
    console.log(direction);
}

Playground

kelsny
  • 23,009
  • 3
  • 19
  • 48
  • Oh no what a mess. Thanks! I cam up with a simmilar solution which I'm using now. Thanks for explaining and solving – Moritz Apr 11 '23 at 15:19