1

I do follow this idea to create an Enum(-like) structure in JavaScript with a Symbol:

const DATA = Object.freeze({
    GOOD:         Symbol("good"),
    BAD:          Symbol("bad"),
    I_DONT_CARE:  Symbol("i-dont-care"),
});

Now, I want to create a new object using the (unique) symbols to refer to each item and extend data about it:

const TRANSLATION = Object.freeze({
    [DATA.GOOD]:         "Gut",
    [DATA.BAD]:          "Schlecht",
    [DATA.I_DONT_CARE]:  "Egal",
});

This is indeed valid even though e.g. TypeScript did not believe that and complained, but this is being fixed too.

However, the problem now is that if I do Object.entries(TRANSLATION) (or Object.keys(TRANSLATION) / Object.values(TRANSLATION)) to further edit and convert that object, I do get an empty object.

It seems all Symbols are ignored when iterating:

myenum = {
  "bla": "blub",
  [Symbol("test")]: "testData"
}
console.log(Object.keys(myenum));

I do not want that, I just want to use the symbols as unique keys, so I can store/"map" more information to a specific entry.

rugk
  • 4,755
  • 2
  • 28
  • 55
  • That's the way those methods are defined to work. – Pointy Dec 02 '20 at 22:10
  • It's sort of for consistency. Pre-ES6 we had `Object.keys` but not symbols, so `Object.keys(obj)` would have to work *differently* in ES5 and ES6, which makes the code unstable. `Object.entries` was added later than that, however if it iterated over symbols it would be inconsistent with `Object.keys`. There is `Object.getOwnPropertySymbols` to extract only the symbols or `Object.getOwnPropertyDescriptors` for everything. – VLAZ Dec 02 '20 at 22:12
  • 1
    Also, there is `Reflect.ownKeys` (I keep forgetting about `Reflect` as I don't use it often). – VLAZ Dec 02 '20 at 22:15

3 Answers3

2

Symbols are not enumerable in for...in iterations. In addition, Object.getOwnPropertyNames() will not return symbol object properties, however, you can use Object.getOwnPropertySymbols() to get these. - MDN

It's how Symbols work. They won't be iterated over. So using Object.keys() will exclude them from the result.

However you can use Object.getOwnPropertySymbols() to get the Symbols from the object and then combine them with Object.keys() to create a definitive list of all keys.

Check the example below to see it work. Do note that the stack snippet console interprets the Symbol as null. However when you open the console of your browser you should see the result as it actually is.

const myenum = {
  "bla": "blub",
  [Symbol("foo")]: "bar",
  [Symbol("roger")]: "roger"
}

const iterableKeys = Object.keys(myenum);
const symbolKeys = Object.getOwnPropertySymbols(myenum);
const keys = [...iterableKeys, ...symbolKeys];

console.log(keys);

const values = keys.map(key => myenum[key]);

console.log(values);

And as a shorter alternative which VLAZ suggested using Reflect.ownKeys

Its return value is equivalent to Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target)).

Which means it already combines the Object.keys() (but using Object.getOwnPropertyNames) and Object.getOwnPropertySymbols() by default.

const myenum = {
  "bla": "blub",
  [Symbol("foo")]: "bar",
  [Symbol("roger")]: "roger"
}

const keys = Reflect.ownKeys(myenum);

console.log(keys);

const values = keys.map(key => myenum[key]);

console.log(values);
Emiel Zuurbier
  • 19,095
  • 3
  • 17
  • 32
0

Symbols are not iterated over. From my point of view they are more useful for hiding properties and generating unique re-used values.

I'd recommend using the TypeScript implementation, I think it's been iterated and battle-tested.

TypeScript has enums support:

enum Direction {
  Up = 1,
  Down,
  Left,
  Right,
}

And it get's transpiled to the following JS:

var Direction;
(function (Direction) {
    Direction[Direction["Up"] = 1] = "Up";
    Direction[Direction["Down"] = 2] = "Down";
    Direction[Direction["Left"] = 3] = "Left";
    Direction[Direction["Right"] = 4] = "Right";
})(Direction || (Direction = {}));

// resulting object 
// Direction = {
//   1: "Up",
//   2: "Down",
//   3: "Left",
//   4: "Right",
//   "Up": 1,
//   "Down": 2,
//   "Left": 3,
//   "Right": 4
// }

If you switch from Symbol to unique strings the problem will be solved.

Vitalii
  • 2,071
  • 1
  • 4
  • 5
-1

Ah, okay, the reason is: Symbols are not enumerable. Fortunately, there is Object.getOwnPropertySymbols() that at least returns an array of all Symbols, similar to Object.keys.

With that, you can of course also "emulate" more advanced features like OBject.values in the oldish pre-ECMAScript 2017 way:

const myenum = {
  "bla": "blub",
  [Symbol("test")]: "testData",
  [Symbol("goodsymbol")]: "baddata"
}
console.log(Object.getOwnPropertySymbols(myenum).map((mysymbol) => {
  const value = myenum[mysymbol];
  return [mysymbol, value];
}));
rugk
  • 4,755
  • 2
  • 28
  • 55
  • Corrections symbols *are* enumerable. However, they aren't iterated over. [The enumerable property is still taken into account in different cases](https://stackoverflow.com/questions/31029612/why-can-es6-symbol-properties-be-made-enumerable-by-object-defineproperty). – VLAZ Dec 02 '20 at 22:32
  • 1
    Also, if you want to iterate over all keys, I'd suggest `Reflect.ownKeys(myenum)`. It also happens to be shorter. – VLAZ Dec 02 '20 at 22:32