Full featured, extensible enumerations for javascript.
npm install enumerated_types
Download the package from here.
The js
folder contains two files: enumerated_types.js
and
enumerated_module.js
.
To use enumerated_types.js
load it from an HTML script tag:
<script type='text/javascript' src='/js/enumerated_types.js'></script>
To use enumerated_module.js
import the required
objects/functions:
import * as enumeration from '/js/enumerated_module.js'
import {Enum} from '/js/enumerated_module.js'
The enumeration
package provides a base object,
EnumerationBase
, on which to base enumerated types.
The following methods are available on all enumerated types:
Method | Description | Default Implementation |
---|---|---|
first() | Returns the first value in the enumeration | no |
fromInt(i) | Returns an enumeration given an integer | no |
isEqualTo(other) | Tests two enumerations for equality | yes |
isGreater(other) | Test if one enumeration is greater than another | yes |
isGreaterOrEqualTo(other) | Test if one enumeration is greater than or equal to another | yes |
isLess(other) | Test if one enumeration is less than another | yes |
isLessOrEqualTo(other) | Test if one enumeration is less than or equal to another | yes |
iterator(from, to) | Gives an iterator between the (optional) given enumeration values | yes |
last() | Returns the last value of the enumeration | no |
methods(methods_object) | Adds additional methods to the enumerated type | yes |
next() | Returns the next enumeration value in the sequence | yes |
previous() | Returns the previous enumeration value in the sequence | yes |
toInt() | Converts an enumeration value to its integer representation | no |
toString() | Converts an enumeration value to a string representation | no |
When creating your own enumerated types, the methods marks “no” under ‘Default Implementation’ must be implemented. All other methods may be overridden if required.
Use the Enum
function to create a set of constants:
Color = Enum(
'RED',
'ORANGE',
'YELLOW',
'GREEN',
'BLUE',
'INDIGO',
'VIOLET'
)
Constants can be converted to and from their interger equivalents:
Color.fromInt(3) // Color.GREEN
Color.GREEN.toInt() // 3
And you can also get the string representation:
Color.RED.toString() // 'RED'
Color.VIOLET.toString() // 'VIOLET'
This allows you to use the constants as if they were object keys.
someObj = {
Color.RED : 'Something having to do with red.',
Color.BLUE : 'Something to do with blue.',
}
In the above example the actual keys are the strings RED
and
BLUE
because javascript calls toString()
automatically when
an object is used as a key.
You can navigate forwards and backwards through the sequence using
next()
and previous()
. Going past the end of the sequence
throws and error:
Color.RED.next() // Color.ORANGE
Color.INDIGO.previous() // Color.BLUE
Color.RED.previous() // Error!
Color.VIOLET.next() // Error!
To get around that you can get an iterator()
and loop through
the values with a for
loop or with forEach
.
// Prints all the colors to the console.
for (let color of Color.iterator()) {
; console.log(color.toString())
}
// Also prints all the colors to the console.
Color.iterator().forEach(color => console.log(color.toString()))
The iterator()
method takes two parameters from
and to
, both
optional, so you can iterate though a subset of the values.
Color.iterator(Color.GREEN, Color.INDIGO)
.forEach(color => console.log(color.toString()))
And can even be used to iterate though the values in reverse order if desired.
Color.iterator(Color.last(), Color.first())
.forEach(color => console.log(color.toString()))
The constants can be compared in a number of ways.
Color.RED.isLess(Color.BLUE) // true
Color.VIOLET.isLess(Color.BLUE) // false
Color.VIOLET.isGreater(Color.BLUE) // true
Color.VIOLET.isGreater(Color.VIOLET) // false
Color.VIOLET.isGreaterOrEqual(Color.VIOLET) // true
Color.RED.isEqual(Color.GREEN) // false
Color.RED.isEqual(Color.RED) // true
Color.RED === Color.GREEN // false
Color.RED === Color.RED // true
Finally, the type can be extended with addtional methods:
Answers = Enum('YES', 'NO', 'MAYBE').methods({
toLowerCase() {
return this.toString().toLowerCase();
},
capitalize() {
const as_string = this.toString();
const first = as_string[0];
const rest = as_string.substring(1).toLowerCase();
return `${first}${rest}`;
}
})
console.log(Answers.YES.toLowerCase()); // yes
console.log(Answers.Maybe.capitalize()); // Maybe
You can call methods
at most once. After that the prototype
object is frozen and can’t be modified further.
The EnumObjects
function works in much the same way as Enum
but instead of a list of strings it takes an object as input. Each
key becomes a constant and, unlike Enum
each contant can have
its own additional properties and methods.
Animal = E.EnumObjects({
DOG : {
speak() {
console.log('woof!');
}
},
CAT : {
speak() {
console.log('meow!');
}
},
GIRAFFE : {}
}).methods({
speak() {
console.log('...');
}
})
Animal.DOG.speak() // woof!
Animal.CAT.speak() // meow!
Animal.GIRAFFE.speak() // ...
Animal.iterator().forEach(a => a.speak())
In particular, notice that we can use methods
to define default
methods which will apply to all constants that don’t specifically
override it: DOG
and CAT
use their own behaviour while
GIRAFFE
uses the default.
Flags are like enumerated constants except, instead of being sequential, each flag’s value is a power of 2.
Options = Flags('A', 'B', 'C', 'D')
Options.A.toInt() // 1
Options.B.toInt() // 2
Options.C.toInt() // 4
Options.D.toInt() // 8
This means you can combine multiple flags into a single integer
value using the bitwise OR (|
) operator:
Options.A | Options.B | Options.D; // 11
Flag types have an additional method, listFromInt()
, which
returns a list of all options represented by a given integer.
Options.listFromInt(Options.A | Options.B | Options.D); // [Options.A, Options.B, Options.D]
Options.listFromInt(12) // [Options.C, Options.D]
Options.listFromInt(0) // []
Options.listFromInt(16) // Error! Out of range.
Another type of enumeration are values constrained to a particular
range: values between 1 and 10, for instance. In this case it’s
actually the number we’re interested in not a symbolic name and
there are, generally, too many possible values to create them as
contants. Instead we can create an EnumeratedRange
.
SmallNum = EnumeratedRange(1, 10, 'SmallNum')
The resulting value, SmallNum
in this example, can then be used
to construct values in this range. The third parameter is
optional and is only relevant when calling toString()
. Trying to
create a value outside of the allowed range throws an error.
x = SmallNum(1)
y = SmallNum(5)
z = SmallNum(10)
error1 = SmallNum(0) // Error!
error2 = SmallNum(11) // Error!
Even though the values don’t exist as constants you can still get an iterator over all the possible values:
// Prints all SmallNum values from 1 to 10
SmallNum.iterator().forEach(n => console.log(n))