Skip to content

Instantly share code, notes, and snippets.

@bbars
Created February 21, 2022 17:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bbars/2ca0c2bd2b20544f435f208565c1dd71 to your computer and use it in GitHub Desktop.
Save bbars/2ca0c2bd2b20544f435f208565c1dd71 to your computer and use it in GitHub Desktop.
Base class to implement enums in JS. Name is string, enum is an instance of derived class of Enum, value is a property
class Enum {
constructor(name, value) {
if (this.constructor._nameValue.has(name)) {
throw new Error(`Duplicate name: ${name}`);
}
if (this.constructor._valueName.has(value)) {
throw new Error(`Duplicate value: ${value}`);
}
Object.defineProperties(this, {
name: {
value: name,
enumerable: true,
},
value: {
value: value,
enumerable: true,
},
});
this.constructor._nameValue.set(name, value);
this.constructor._valueName.set(value, name);
}
static get _nameValue() {
Object.defineProperty(this, '_nameValue', {
value: new Map(),
});
return this._nameValue;
}
static get _valueName() {
Object.defineProperty(this, '_valueName', {
value: new Map(),
});
return this._valueName;
}
valueOf() {
return this.value;
}
toString() {
return `[${this.constructor.name} ${this.name}]`;
}
static defineOne(name, value) {
name = String(name);
return this[name] = new this(name, value);
}
static defineList(...names) {
let value = 0;
const res = [];
for (const name of names) {
res.push(this.defineOne(name, value++));
}
return res;
}
static defineMap(map) {
const res = [];
for (const name in map) {
res.push(this.defineOne(name, map[name]));
}
return res;
}
static findByValue(value) {
if (!this._valueName.has(value)) {
throw new Error(`Unable to find enum ${this.name} by value`);
}
const name = this._valueName.get(value);
return this[name];
}
toJSON() {
return this.name;
}
static fromJSON(jsonValue) {
if (!this._nameValue.has(jsonValue)) {
throw new Error(`Unable to find enum ${this.name} by name '${jsonValue}'`);
}
return this[jsonValue];
}
}
// Mouse buttons:
class MouseButton extends Enum {}
MouseButton.defineList('LEFT', 'MIDDLE', 'RIGHT');
// it's also good idea to freeze enum after all definitions are done:
Object.freeze(MouseButton);
console.log(MouseButton.LEFT, MouseButton.MIDDLE, MouseButton.RIGHT); // instances of MouseButton
console.log(MouseButton.findByValue(1)); // MouseButton.MIDDLE
// Cartesian quadrants:
class Quadrant extends Enum {
get angle() {
return (this.value - 1) * Math.PI / 2;
}
static getByAngle(angle) {
const TAU = Math.PI * 2;
angle = (angle % TAU + TAU) % TAU;
if (angle < Math.PI * 0.5) {
return this.I;
}
if (angle < Math.PI * 1.0) {
return this.II;
}
if (angle < Math.PI * 1.5) {
return this.III;
}
if (angle < Math.PI * 2.0) {
return this.IV;
}
throw new Error(`Unable to find quadrant by angle: ${angle}`);
}
static getByPos(x, y) {
if (!x && !y) {
throw new Error(`Unable to find quadrant by pos: ${x},${y}`);
}
const angle = Math.atan2(y, x);
return this.getByAngle(angle);
}
}
Quadrant.defineMap({
'I': 1,
'II': 2,
'III': 3,
'IV': 4,
});
console.log(Quadrant.I, Quadrant.I.angle);
console.log(Quadrant.II, Quadrant.II.angle);
console.log(Quadrant.III, Quadrant.III.angle);
console.log(Quadrant.IV, Quadrant.IV.angle);
console.log(Quadrant.getByAngle(0)); // I
console.log(Quadrant.getByAngle(Math.PI)); // III
console.log(Quadrant.getByPos(-1, 1)); // II
console.log(Quadrant.getByPos(1, -1)); // IV
const c3 = Quadrant.III;
const json = JSON.stringify(c3); // "III"
console.log(Quadrant.fromJSON(JSON.parse(json)) === Quadrant.III); // true
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment