enum-next
v1.0.3
Published
JS Implementation of the Java Enumerable type
Downloads
58
Readme
Enumerated Type
JS Implementation of the Java Enumerable type
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 toconstant.val
- thinkPLANETS.VENUS.val
- Keys have to be strings and valid
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 toconstant.val
- thinkPLANETS.VENUS.val
- Keys have to be strings and valid
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 optionsclean
{Boolean}: Whether to do a clean concat or not. Clean concats allow you to standardize behaviours via thebehaviour
option property and have new $id properties assigned, but the new Enum constants will not be equal to the constants from the constituent (passed) enumssymbolOnly
{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 ofObject.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 nobehaviour
is provided, the default behaviour will be the equivalent ofObject.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.