ts-enum-tools
Version:
Tools for evaluating TypeScript enum types
206 lines (182 loc) • 5.87 kB
text/typescript
/** Interface describing the tools for flag enums */
export interface EnumFlagsTool<E, e> {
state: e, // Returns map of T/F values
eql: (a: E) => boolean;
has: (a: E) => boolean;
any: (a: E) => boolean;
toArray: () => string[];
toString: () => string;
}
/** Static properties on the function returned by EnumFlagsType<E,e> */
export interface EnumFlagsFunc<E, e> {
(val?): EnumFlagsTool<E, e>,
toArray: () => { key: string, val: number }[];
val: e,
key: e,
}
/** Interface describing the tools for string enums */
export interface EnumStringsTool<E, e> {
state: e, // Returns map of T/F values
equals: (a: E) => boolean;
toStringKey: () => string;
toStringVal: () => string;
}
/** Static properties on the function returned by EnumStringsType<E,e> */
export interface EnumStringsFunc<E, e> {
(str?): EnumStringsTool<E, e>,
toArray: () => { key: string, val: string }[];
val: e,
key: e,
}
/**
* Fastest methods are these simple no frills comparison tests.
*/
export var EnumFlagsTest = {
has: function(val, flags) {
return ((+val & +flags) === +flags);
},
any: function(val, flags) {
return !!(+val & +flags);
},
eql: function(val, flags) {
return (+val === +flags);
}
}
/**
* This alternative using prototype method may be faster - WTH ?
*/
export var EnumFlagsTestAlt = function(val) {
this.val = val;
};
EnumFlagsTestAlt.prototype.has = function(flags) {
return ((+this.val & +flags) === +flags)
};
/**
* Returns a tools assortment for flag enums, and optionally implements them as a property of
* Number.prototype (named getter). The tools automatically bind to each unique Number.
*/
export function EnumFlagsType<E, e>(enumeration, prop?: string): EnumFlagsFunc<E, e> {
var keys = {};
var hash = Object.keys(enumeration).reduce(function(obj, k) {
// Excludes bi-directional numeric keys
if (isNaN(<any>k)) {
obj[k] = +enumeration[k];
}
keys[k] = k;
return obj;
}, {});
// New instance created per each binding
var State = function(methods) {
this.methods = methods
};
Object.keys(hash).forEach(function(k) {
Object.defineProperty(State.prototype, k, { get: function() {
return !!hash[k] ? ((this.methods.val & hash[k]) === hash[k]) : !this.methods.val;
}});
});
// New instance created per each binding
var Methods = function(val) {
this.val = +val;
this.state = new State(this);
};
Methods.prototype.eql = function(flags) {
return (this.val === +flags);
};
Methods.prototype.has = function(flags) {
return (+(this.val & +flags) === +flags);
};
Methods.prototype.any = function(flags) {
return !!(this.val & +flags);
};
Methods.prototype.toArray = function() {
return Object.keys(hash).filter(function(k) {
return !!hash[k] && ((this.val & hash[k]) === hash[k]); // all bits weed out combos
}.bind(this));
};
Methods.prototype.toString = function() {
return Object.keys(hash).filter(function(k) {
return !!hash[k] && ((this.val & hash[k]) === hash[k]);
}.bind(this)).join(' | ');
};
// This either runs in the context of a primitive or a value must be provided
var BindValue: any = function BindValue(val?: number): EnumFlagsTool<E, e> {
return new Methods((val === undefined) ? this : val);
};
BindValue.val = hash;
BindValue.key = keys;
BindValue.toArray = function() {
let arr = [];
Object.keys(hash).map(function(k) {
arr.push({ key: k, val: hash[k] });
});
};
// If property name is given, a number prototype is set
if (prop) {
Object.defineProperty(Number.prototype, prop, { get: BindValue });
}
return BindValue;
}
/**
* Returns a tools assortment for string enums, and optionally implements them as a property of
* String.prototype (named getter). The tools automatically bind to each unique string.
*/
export function EnumStringsType<E, e>(enumeration, prop?: string, validKeysFilter?: Function): EnumStringsFunc<E, e> {
var hash = {};
var keys = {};
if (validKeysFilter) {
Object.keys(enumeration).forEach(function(k) {
if (validKeysFilter(k)) {
hash[k] = enumeration[k];
keys[k] = k;
}
});
} else {
Object.keys(enumeration).forEach(function(k) {
hash[k] = enumeration[k];
keys[k] = k;
});
}
// New instance created per each binding
var State = function(methods) {
this.methods = methods
};
Object.keys(hash).forEach(function(k) {
Object.defineProperty(State.prototype, k, { get: function() {
return (this.methods.str === hash[k]);
}});
});
// New instance created per each binding
var Methods = function(str) {
this.str = str.toString();
this.state = new State(this);
};
Methods.prototype.equals = function(str) {
return (this.str === str);
};
Methods.prototype.toStringKey = function() {
for(var k in hash) {
if(hash[k] === this.str) {
return k;
}
}
};
Methods.prototype.toStringVal = function() {
return this.str;
};
// This either runs in the context of a primitive or a string must be provided
var BindString: any = function (str?: string): EnumStringsTool<E, e> {
return new Methods((str === undefined) ? this : str);
};
BindString.val = hash;
BindString.key = keys;
BindString.toArray = function() {
return Object.keys(hash).map(function(k) {
return { key: k, val: hash[k] };
});
};
// If property name is given, a string prototype is set
if (prop) {
Object.defineProperty(String.prototype, prop, { get: BindString });
}
return BindString;
}