Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ES6 read-only enums that can map value to name

I would like to define an enum-like structure in JS, but have two requirements:

  1. The values be read-only, i.e. no users can assign to them.
  2. The values (0, 1, 2, ...) can be mapped back into the names (as with Java's name method)

The methods I know to create enums like this typically meet one requirement or the other, not both.

I've tried:

const MyEnum = {
  a: 0,
  b: 1,
  c: 2
};

The enum itself is constant, but the values are still mutable and I can't map values back to names efficiently.

When writing an enum in Typescript, it outputs:

var MyEnum;
(function (MyEnum) {
    MyEnum[MyEnum["a"] = 0] = "a";
    MyEnum[MyEnum["b"] = 1] = "b";
    MyEnum[MyEnum["c"] = 2] = "c";
})(MyEnum || (MyEnum = {}));

This can map both ways, but still doesn't have constant values.

The only option I've found that meets both requirements would be using getters on a class:

class MyEnum {
  get a() {
    return 0;
  }
  ...
}

This method dramatically restricts the legal names and has a lot of overhead, especially in browsers that don't inline getters well (or can't).

@Shmiddty suggested freezing an object:

const MyEnum = Object.freeze({
  a: 0,
  b: 1,
  c: 2
});

This meets the constant requirement well, but doesn't provide a great way to map values back to names.

I could write a helper that builds the reverse mapping like:

function reverseEnum(enum) {
  Object.keys(enum).forEach(k => {
    enum[enum[k]] = k;
  });
}

But any kind of programmatic solution to generate the reverse mapping will run into problems if the original object is frozen or otherwise actually constant.

Is there a clean, concise solution to this in JS?

like image 449
ssube Avatar asked Jun 08 '15 18:06

ssube


People also ask

Can you map an enum?

Since a JavaScript enum is just an object, you can iterate over an object using map() and Object.

Can you assign values to enums?

Enum ValuesYou can assign different values to enum member. A change in the default value of an enum member will automatically assign incremental values to the other members sequentially. You can even assign different values to each member.

Does ES6 have enums?

ES6 provides different types of features to the user, in which that enum is one of the features that are provided by the ES6. Basically, enum allows the developer to define the set of name constants that the developer wants as per their requirement.

Can two enum names have the same value?

Two enum names can have same value. For example, in the following C program both 'Failed' and 'Freezed' have same value 0.


1 Answers

This does a pretty good job, IMHO.

function Enum(a){
  let i = Object
    .keys(a)
    .reduce((o,k)=>(o[a[k]]=k,o),{});

  return Object.freeze(
    Object.keys(a).reduce(
      (o,k)=>(o[k]=a[k],o), v=>i[v]
    )
  );
} // y u so terse?

const FOO = Enum({
  a: 0,
  b: 1,
  c: "banana"
});

console.log(FOO.a, FOO.b, FOO.c);            // 0 1 banana
console.log(FOO(0), FOO(1), FOO("banana"));  // a b c

try {
  FOO.a = "nope";
}
catch (e){
  console.log(e);
}
like image 68
Shmiddty Avatar answered Nov 09 '22 23:11

Shmiddty