Another take on enums: Nums

About 13 years ago, we discussed the possibility to have enums (where enum is a reserved word) and the whole point of the discussion rotated around the fact these must be unique: Enums?

Enter TypeScript, and what the majority of the community settled down with, is a simple dictionary-like namespace where each field gets an incremental value:

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

The TS syntax is mostly exploiting objects literal default values, and the enum type infer the next value from the previous one, if defined.

I understand the whole meaning of enum might lead (it does) to different PLs and bike shedding conversations, which is why I am proposing a new primitive here that simplifies such distinction or meaning: Nums!

In its most basic form, Nums would create an immutable dictionary that simply assign an integer to its properties:

const Direction = new Nums(
  'Up',
  'Down',
  'Left',
  'Right',
);

It remains to discuss how to define any value in between and here I have three proposals:

Map like definition

const Direction = new Nums(
  ['Up', 1],
  'Down',
  'Left',
  'Right',
);

This would allow to define an initial starting point to follow up, and the constraint remains: it's going to be a number (unsigned integer) so that it can be incremented for any other field.

Object like definition

const Direction = new Nums(
  { Up: 1 },
  'Down',
  'Left',
  'Right',
);

This works the same as the previous proposal except it uses an object literal notation instead where the constrain is that it should have one own key only and an integer as value.

Literal proposal

let i = 1;
const Direction = new Nums({
  Up: i++,
  Down: i++,
  Left: i++,
  Right: i++,
});

This shows that enums like references in JS are trivial, done the TypeScript way, but my interest is into the Nums (I don't have a strong opinion about this name neither) premises:

  • all fields MUST have an unsigned integer value
  • no field can have another field value within the same Nums instance
  • all Nums instances can be directly resolved or processed as an integer, making these faster and less env pollution prone than dictionaries, as nothing should be able to interfere with their values, not even an Object.freeze overload or an Object.prototype pollution ... safety embedded

Pros

  • transpilers can transform Nums instances, when statically accessed, into integers
  • cross realm/thread operations easily survive postMessage dance or serialization, still representing their value as intended in the receiving side of affairs
  • with easy enough orchestration, these can be also used for bitwise related operations and still grant all of the above, without being unique references that cannot play any role in bitwise related operations

Thoughts?

naive polyfills here:

Map like definition

class Nums {
  constructor(...args) {
    let i = 0, field;
    for (field of args) {
      if (typeof field !== 'string')
        [field, i] = field;
      this[field] = i++;
    }
  }
}

const Direction = new Nums(
  ['Up', 1],
  'Down',
  'Left',
  'Right',
);

console.log(Direction);

Object like definition

class Nums {
  constructor(...args) {
    let i = 0, field;
    for (field of args) {
      if (typeof field !== 'string') {
        const [k] = Object.keys(field);
        [field, i] = [k, field[k]];
      }
      this[field] = i++;
    }
  }
}

const Direction = new Nums(
  { Up: 1 },
  'Down',
  'Left',
  'Right',
);

console.log(Direction);

Literal

class Nums {
  constructor(object) {
    Object.assign(this, object);
  }
}

let i = 1;
const Direction = new Nums({
  Up: i++,
  Down: i++,
  Left: i++,
  Right: i++,
});

console.log(Direction);

I think GitHub - rbuckton/proposal-enum: Proposal for ECMAScript enums was the proposal most recently presented in a TC39 meeting

2 Likes

OK then, after a quick read, I find that a definitively better proposal than this one ... so, how comes it's stuck in 6 years ago and nothing landed in the meantime?

Maybe in here (this proposal) there's some easy to unlock blocker about that?

edit for instance, I find the whole Enum namespace irrelevant, if that was a concern at any point in time ... YAGNI, imho.