enummer v0.1.0
enummer
A 'real' Enum implementation for JavaScript with typesafety, ordinal support and comparison support. inspired by Java. Typed with Flow and supports ES-modules.
Installation
Simply do: npm install enummer
.
What is it?
A proper Enum implementation for Javascript. Unlike other Enum implementations, this one follows the Java implementation very strongly and supports:
- Ranked Enum constants (ordinals)
- Support for
instanceof
andisPrototypeof
checks. - Support for native comparisons with the
<
,>
and=== operatiors
. - Typesafety. Built with flow and throws runtime exceptions.
- Immutability. All Enum constants are immutable and accesses happens through getters.
Example 1
import Enum from "enummer";
const Day = Enum.define("Day",
"MONDAY", "TUESDAY", "WEDNESDAY"
);
Day.toString() // Day { MONDAY, TUESDAY, WEDNESDAY }
Day.MONDAY instanceof Day // true
Day.MONDAY < Day.TUESDAY // true
Day.WEDNESDAY === Day.WEDNESDAY // true
Day.values() // [ MONDAY {}, TUESDAY {}, WEDNESDAY {} ]
Day.MONDAY.getDeclaringClass() // Day { MONDAY, TUESDAY, WEDNESDAY }
Enum.isPrototypeOf(Day) // true
Example 2 - with explicit ordinals
import Enum from "enummer";
const FavoriteColor = Enum.define("FavoriteColor",
"BLUE(2)", "RED(1)", "YELLOW(10)"
);
FavoriteColor.toString() // FavoriteColor { MONDAY, TUESDAY, WEDNESDAY }
FavoriteColor.BLUE > FavoriteColor.RED // true, Blue has higher ordinal than red.
Example 3 - as an object with explicit ordinals
import Enum from "enummer";
const FavoriteColor = Enum.define("FavoriteColor", {
BLUE: 2,
RED: 1,
YELLOW: 10
});
FavoriteColor.toString() // FavoriteColor { MONDAY, TUESDAY, WEDNESDAY }
FavoriteColor.BLUE > FavoriteColor.RED // true, Blue has higher ordinal than red.
Example 4 - with floating point numbers an 'Infinity'
import Enum from "enummer";
const FavoriteColor = Enum.define("FavoriteColor", {
BLUE: 2.2,
RED: 2.1,
YELLOW: Infinity
});
FavoriteColor.BLUE > FavoriteColor.RED // true, Blue has higher ordinal than red.
FavoriteColor.YELLOW > FavoriteColor.BLUE // true, no value would ever be larger than 'YELLOW'.
Changelog:
v0.1.0:
- Added support for browsers that doesn't support
Object.entries()
andObject.keys()
. Previous versions didn't work in Safari due to missing support forObject.entries()
.
v0.0.9:
- Fixed a few typing errors.
v0.0.6:
- Enummer now supports floating point numbers for ordinals as well as setting
Infinity
as ordinal value.
v0.0.5:
- Enummer now supports an alternative syntax for enum constants. They can now be written as objects. For instance:
Enum.define("MyEnum", {TODAY: 1, YESTERDAY: 2})
Is now acceptable. Note that you need to declare ordinals (a number) for each key.
v0.0.4:
- Previous versions wouldn't work in environments without native
class
support. This is fixed now. Added a missing semicolon.
v0.0.3:
- Updated package dependency and keywords.
v0.0.2:
- First release!
Usage
Import it in your project like this:
import Enum from "enummer" // (standard) Transpiled to ES5, inlined dependencies.;
// or
import Enum from "enummer/external" // Transpiled to ES5, external dependencies.
// or
import Enum from "enummer/native" // Transpiled to ES5, native ES-modules, inlined dependencies.
// or
import Enum from "enummer/native-external" // Transpiled to ES5, native ES-modules external dependencies.
// or
import Enum from "enummer/typed" // Flow typings, ES-modules, external dependencies.
Documentation
Construction
The Enum
class is abstract and can't be instanced directly. Instead, use the static define
method for constructing enums:
const MyEnum = new Enum("MyEnum", "a", "b", "c") // Throws a SyntaxError, Enum is an abstract class.
const MyEnum = Enum.define("MyEnum", "a", "b", "c") // Works!
Ordinals:
The ranking order of your Enum types will by default increase with 1 for each constant. For instance:
const MyEnum = Enum.define("MyEnum", "a", "b", "c");
MyEnum.a.ordinal === 0; //true
MyEnum.b.ordinal === 1; //true
MyEnum.c.ordinal === 2; //true
However, you can change this ordering however you like. Consider the following:
const MyEnum = Enum.define("MyEnum", "a(2)", "b", "c");
MyEnum.a.ordinal === 2; //true
MyEnum.b.ordinal === 3; //true
MyEnum.c.ordinal === 4; //true
Simply attach (n)
to each constant where n
equals the rank (ordinal). For instance, a(2)
gets the ordinal 2
.
All remaining values will have an increasing order in relation to that.
You can also set the ordering however you like:
const MyEnum = Enum.define("MyEnum", "a(2", "b(5)", "c(10)");
MyEnum.a.ordinal === 2; //true
MyEnum.b.ordinal === 5; //true
MyEnum.c.ordinal === 10; //true
And even:
const MyEnum = Enum.define("MyEnum", "a(2", "b(5)", "c");
MyEnum.a.ordinal === 2; //true
MyEnum.b.ordinal === 5; //true
MyEnum.c.ordinal === 6; //true
Note that you can define your enum constants with objects instead. Using that notation, all enum constants needs to have an associated ordinal:
const MyEnum = Enum.define("MyEnum", {
MONDAY: 1,
TUESDAY: 2,
WEDNESDAY: 3
});
Comparisons
The ordinals allows us to do classic comparisons between Enum constants. For instance, consider the following:
const FavoriteColor = Enum.define("FavoriteColor",
"BLUE(2)", "RED(1)", "YELLOW(10)"
);
FavoriteColor.BLUE > FavoriteColor.RED // true, BLUE has higher ordinal than RED.
FavoriteColor.YELLOW > FavoriteColor.BLUE // true, YELLOW has higher ordinal than BLUE.
Two Enum constants cannot have the same ordinal. This is because the ordinal is often stored in a database as representative of the Enum constant. Thus, two enum constants are only equal if there is referential equality:
FavoriteColor.BLUE === FavoriteColor.BLUE // true
Type safety
You can only construct a new Enum Type with strings. This is because the strings will be used as the names of the inner classes.
For instance, this will throw a TypeError
:
const MyEnum = Enum.define("MyEnum", 2, 10, 15); // Throws a TypeError
const MyEnum = Enum.define(SomeObject, "a", "b", "c") // Throws a TypeError, first argument must be a string.
instanceof
and isPrototypeOf
Because the static define
method on the abstract Enum class generates a new Baseclass with subclasses for each given Enum constant, enummer generates 'real' enums that makes it possible to do type assertions. For instance, consider this Flow example:
const Day = Enum.define("Day", "MONDAY", "TUESDAY", "WEDNESDAY");
Day.MONDAY instanceof Day // true.
function iRequireADayEnum (day: Day) {
if (!(day instanceof Day)) throw new Error();
console.log("wee!");
}
iRequireADayEnum(Day.MONDAY); // Prints 'wee!' to the console.
Generated Enum types has Enum
as their prototype. That means that the following is true:
const MyEnum = Enum.define("MyEnum", "a", "b");
Enum.isPrototypeOf(MyEnum) // true