Flagged Enum
A factory to create a handy flagged enum
by KooiInc MeHere
CSS
body {
font: normal 14px/17px calibri, verdana, arial;
margin: 2rem;
a[target] {
text-decoration: none;
font-weight: bold;
&:hover {
text-decoration: underline;
}
&:before {
content: "↗";
display: inline-block;
margin-right: 3px;
}
}
}
JavaScript
/*
* Notes:
* The proxy handler enables case insensitive property queries
* BigInt is used to enable bitflags with length > 52
* jsFiddle linter does not know BigInt literals ([number]n)
*/
demo();
// ---
function EnumFactory() {
const proxyfy = {
construct(target, args) {
const caseInsensitiveHandler = {
get(target, key) {
return target[key.toUpperCase()] || target[key];
}
};
const proxified = new Proxy(new target(...args), caseInsensitiveHandler );
return Object.freeze(proxified);
},
}
const ProxiedEnumCtor = new Proxy(EnumCtor, proxyfy);
const throwIf = (assertion = false, message = `Unspecified error`, ErrorType = Error) =>
assertion && (() => { throw new ErrorType(message); })();
const hasFlag = (val, sub) => {
throwIf(!val || !sub, "valueIn: missing parameters", RangeError);
const andVal = (sub & val);
return andVal !== BigInt(0) && andVal === val;
};
function EnumCtor(values) {
throwIf(values.constructor !== Array ||
values.length < 2 ||
values.filter( v => v.constructor !== String ).length > 0,
`EnumFactory: expected Array of at least 2 strings`, TypeError);
const base = BigInt(1);
this.NONE = BigInt(0);
values.forEach( (v, i) => this[v.toUpperCase()] = base<<BigInt(i) );
}
EnumCtor.prototype = {
get keys() { return Object.keys(this).slice(1); },
get allKeys() { return Object.keys(this); },
subset(sub) {
const arrayValues = this.keys;
return new ProxiedEnumCtor(
[...sub.toString(2)].reverse()
.reduce( (acc, v, i) => ( +v < 1 ? acc : [...acc, arrayValues[i]] ), [] )
);
},
getLabel(enumValue) {
const tryLabel = Object.entries(this).find( value => value[1] === enumValue ).shift();
return !enumValue || !tryLabel ?
"getLabel: no value parameter or value not in enum" :
tryLabel;
...