UNPKG

typhonjs-escomplex-commons

Version:
176 lines (159 loc) 4.3 kB
/** * This is an abstract class that is not intended to be used directly. Extend it to turn your class into an enum * (initialization is performed via `MyClass.initEnum()`). * * Modified from source provided by enumify (license unlisted / public domain) * @see https://github.com/rauschma/enumify */ export default class Enum { /** * `initEnum()` closes the class. Then calling this constructor throws an exception. * * If your subclass has a constructor then you can control what properties are added to `this` via the argument you * pass to `super()`. No arguments are fine, too. * * @param {object} instanceProperties - Provides initial properties. */ constructor(instanceProperties = void 0) { // new.target would be better than this.constructor, but isn’t supported by Babel if ({}.hasOwnProperty.call(this.constructor, INITIALIZED)) { throw new Error('Enum classes can’t be instantiated'); } if (typeof instanceProperties === 'object' && instanceProperties !== null) { s_COPY_PROPERTIES(this, instanceProperties); } } /** * Set up the enum, close the class. * * @param {Array|object} arg - Either an object whose properties provide the names and values (which must be * mutable objects) of the enum constants. Or an Array whose elements are used as the names of the enum constants. * The values are create by instantiating the current class. * * @returns {Enum} */ static initEnum(arg) { Object.defineProperty(this, 'enumValues', { value: [], configurable: false, writable: false, enumerable: true }); if (Array.isArray(arg)) { this._enumValuesFromArray(arg); } else { this._enumValuesFromObject(arg); } Object.freeze(this.enumValues); this[INITIALIZED] = true; return this; } /** * Extracts enum values from an array. * * @param {Array} arr - * @private */ static _enumValuesFromArray(arr) { for (const key of arr) { this._pushEnumValue(new this(), key); } } /** * Extracts enum values from an object. * * @param {object} obj - * @private */ static _enumValuesFromObject(obj) { for (const key of Object.keys(obj)) { const value = new this(obj[key]); this._pushEnumValue(value, key); } } /** * Pushes enum value. * * @param {object} enumValue - * @param {string} name - * @private */ static _pushEnumValue(enumValue, name) { enumValue.name = name; enumValue.ordinal = this.enumValues.length; Object.defineProperty(this, name, { value: enumValue, configurable: false, writable: false, enumerable: true }); this.enumValues.push(enumValue); } /** * Given the name of an enum constant, return its value. * * @param {string} name - An enum name. * * @returns {T} */ static enumValueOf(name) { return this.enumValues.find((x) => x.name === name); } /** * Make enum classes iterable * * @returns {Symbol.iterator} */ static [Symbol.iterator]() { return this.enumValues[Symbol.iterator](); } /** * Default `toString()` method for enum constant. * * @returns {string} */ toString() { return `${this.constructor.name}.${this.name}`; } } // Module private --------------------------------------------------------------------------------------------------- /** * Provides a Symbol to track initialized state. * @type {Symbol} * @ignore */ const INITIALIZED = Symbol(); /** * Copies an objects properties. * * @param {object} target - Target object. * @param {object} source - Source object. * * @returns {object} * @ignore */ function s_COPY_PROPERTIES(target, source) { // Ideally, we’d use Reflect.ownKeys() here, but I don’t want to depend on a polyfill. for (const key of Object.getOwnPropertyNames(source)) { const desc = Object.getOwnPropertyDescriptor(source, key); Object.defineProperty(target, key, desc); } return target; }