1.0.3 • Published 6 years ago

enum-next v1.0.3

Weekly downloads
3
License
MIT
Repository
github
Last release
6 years ago

Enumerated Type

JS Implementation of the Java Enumerable type

npm version Travis Build Status Coverage Status Dependencies Status

Table of Contents

What is an Enumerated Type (Enum)?

In computer programming, an enumerated type ... is a data type consisting of a set of named values called elements, members, enumeral, or enumerators of the type. The enumerator names are usually identifiers that behave as constants in the language. ...

Conceptually, an enumerated type is similar to a list of nominals (numeric codes), since each possible value of the type is assigned a distinctive natural number. A given enumerated type is thus a concrete implementation of this notion. When order is meaningful and/or used for comparison, then an enumerated type becomes an ordinal type.

— "Enumerated type" on Wikipedia

Why should I use Enums?

Enumerated types are useful as iterable, ordinal constants to which behaviour can be attached. They are especially useful for declaring constants in a consistent, expressive way. See this StackOverflow question as for how to use enums.

A non-native JS implementation of Enums such as this one do not however provide the benefits of type-safe or increase of compile-time type checkup. What it does provides however, is a clean interface for declaring semantically meaningful groups of constants, full constant support through ES2015 Symbols, and giving them shared and non-shared state and behaviour.

For example, here is an Enum of the planets of the solar system (translated from this Java documentation example on Enums):

const PLANETS = new Enum(
  [
    'MERCURY', { mass: 3.303e+23, radius: 2.4397e6 },
    'VENUS',   { mass: 4.869e+24, radius: 6.0518e6 },
    'EARTH',   { mass: 5.976e+24, radius: 6.3781e6 },
    'MARS',    { mass: 6.421e+23, radius: 3.3972e6 },
    'JUPITER', { mass: 1.900e+27, radius: 7.1492e7 },
    'SATURN',  { mass: 5.688e+26, radius: 6.0268e7 },
    'URANUS',  { mass: 8.686e+25, radius: 2.5559e7 },
    'NEPTUNE', { mass: 1.024e+26, radius: 2.4746e7 }
  ],
  {
    // universal gravitational constant  (m3 kg-1 s-2)
    G: 6.67300E-11,
    surfaceGravity(){
      return this.G * this.mass / (Math.pow(this.radius, 2));
    },
    surfaceWeight(mass){
      return this.surfaceGravity() * mass;
    }
  }
);

Not only does it provide a clear, visual cue of this being a set of constants, but it also provides a neat interface:

PLANETS.MERCURY; // Symbol {mass: 3.303e+23, radius: 2439700, G: 6.673e-11, …}
PLANETS.MERCURY.$id; // Symbol(MERCURY)
PLANETS.MERCURY.$key; // 'MERCURY'
PLANETS.EARTH.surfaceGravity(); // 9.803
PLANETS.VENUS.mass === PLANETS.JUPITER.mass; // false

It is also easy to switch on an Enum:

switch(value){
case PLANETS.MERCURY:
  // action
  break;
case PLANETS.VENUS:
  // action
  break;

...

default:
  // action
  break;
}

With the symbolOnly argument, one can create a Symbol-only enumerable:

const PLANETS = new Enum(
  [
    'MERCURY',
    'VENUS',  
    'EARTH',  
    'MARS',   
    'JUPITER',
    'SATURN',
    'URANUS',
    'NEPTUNE',
  ],
  true
);
PLANETS.MERCURY // Symbol(MERCURY)
PLANETS.MERCURY.mass // undefined
PLANETS.VENUS // Symbol(VENUS)
...
PLANETS.NEPTUNE // Symbol(NEPTUNE)

Installation

yarn add enum-next

ESM:

import Enum from 'enum-next';

const ENUM = new Enum([...],{...});

ES5:

const Enum = require('enum-next');

const ENUM = new Enum([...],{...});

API

Static Methods

new Enum(constants , behaviour)

// Enum :: [string, \*, ..., string, \*], Object -> Enum

  • constants {Array}: Array containing all of your constants, with the keys at each even index and the member values at each odd index.
    • Keys have to be strings and valid UPPERCASE_CONSTANTS variable names.
    • Values can be anything. Objects will be expanded and their properties bound to the constant - think PLANETS.VENUS.mass. Non-object values will be assigned to constant.val - think PLANETS.VENUS.val
  • behaviour {Object}: Object containing all of the state and behaviour that should be shared by all of the constants. These will be bound to the constants.

Constructor for the Enum type.

new Enum(constants, symbolOnly)

// Enum :: [string, \*, ..., string, \*], Bool -> Enum

  • constants {Array}: Array containing all of your constants, with the keys at each even index and the member values at each odd index.
    • Keys have to be strings and valid UPPERCASE_CONSTANTS variable names.
    • Values can be anything. Objects will be expanded and their properties bound to the constant - think PLANETS.VENUS.mass. Non-object values will be assigned to constant.val - think PLANETS.VENUS.val
  • symbolOnly {boolean}: If all values in constants are strings, will convert all strings in symbol and return an enum of Symbols without any extra properties. Defaults to false.

Constructor for the Enum type. Returns an enum that is only comprised of Symbols.

Enum::concat(enums, options)

// Enum.concat :: [Enum], Object -> Enum

  • enums {Array}: Array of the Enums to concatenate together.
  • options {Object}: Concatenation options
    • clean {Boolean}: Whether to do a clean concat or not. Clean concats allow you to standardize behaviours via the behaviour option property and have new $id properties assigned, but the new Enum constants will not be equal to the constants from the constituent (passed) enums
    • symbolOnly {Boolean}: If truthy, creates a Symbol-only Enum.
    • behaviour {Object}: Behaviour to add to the clean concatenated constants. Ignored if symbolOnly is truthy.

Concatenates multiple enums into a single one. This can be done in two ways:

  • Dirty: Dirty concatenation references the keys from the constituent enums directly; this allows for equality when comparing a key from a constituent Enum with the same key from the concatenated Enum, but additional behaviour cannot be passed, and some $id will overlap. This is a limitation put in place to avoid side-effects by mutating the keys of the constituent enums. In this case, if you want a similar behaviour for all the keys, it is suggested that all the constituent enums have behaviour objects that follow the same interface. symbolOnly will be set to true if all of the constituent enums are Symbol-only Enums. You can always find the agglomerated behaviour of all constituent enums under the Enum.behaviour property, which will be the equivalent of Object.assign(enums[0].behaviour, enums[1].behaviour, ...).
  • Clean: Clean concatenation creates brand new unique keys for the concatenated enumerable, thereby allowing a standardization of the behaviour of all the keys without any side-effect. The downside of clean concatenation is that it does not allow for key equality between a key of a constituent Enum and its analogue in the concatenated Enum. If no behaviour is provided, the default behaviour will be the equivalent of Object.assign(enums[0].behaviour, enums[1].behaviour, ...).

Instance Methods

Enum#iterator()

// Enum.iterator :: * -> Iterator

Returns an iterator for the current Enum. Can be used in conjunction with for..of.

N.B.: It is not necessary to use Enum.iterator to use for..of - the Enum objects are iterable by default :rainbow:

Enum#keys()

// Enum.keys :: * -> [string]

Returns an array of all the constant keys provided to the constructor.

Enum#values()

// Enum.values :: * -> [Symbol]

Returns an array of all the Symbol values. If symbolOnly is specified, primitive Symbols will be returned; Object(Symbol) otherwise.

Enum#entries()

// Enum.values :: * -> [[string, Symbol]]

Returns an returns an array of a given Enum's key, value pairs, in the same order as that provided by a for...of loop. If symbolOnly is specified, primitive Symbols will be returned; Object(Symbol) otherwise.