enum-plus
Version:
A drop-in replacement for native enum. Like native enum but much better!
270 lines • 10.6 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.EnumItemsArray = void 0;
exports.parseKeys = parseKeys;
const enum_item_1 = require("./enum-item");
const utils_1 = require("./utils");
/**
* Enum items array, mostly are simple wrappers for EnumCollectionClass
*
* @template T Type of the initialization data of the enum collection
*
* @class EnumItemsArray
*
* @extends {EnumItemClass<T, K, V>[]}
*
* @implements {IEnumItems<T, K, V>}
*/
class EnumItemsArray extends Array {
/**
* - **EN:** A boolean value indicates that this is an enum items array.
* - **CN:** 布尔值,表示这是一个枚举项数组
*/
// Do not use readonly field here, because don't want print this field in Node.js
// eslint-disable-next-line @typescript-eslint/class-literal-property-style
get [utils_1.IS_ENUM_ITEMS]() {
return true;
}
/**
* Instantiate an enum items array
*
* @memberof EnumItemsArray
*
* @param {T} raw Original initialization data object
* @param {EnumItemOptions<T[K], K, V, P> | undefined} options Enum item options
*/
constructor(raw, options) {
super();
// Do not use class field here, because don't want print this field in Node.js
Object.defineProperty(this, '__raw__', {
value: raw,
enumerable: false,
writable: false,
configurable: false,
});
// Generate keys array
// exclude number keys with a "reverse mapping" value, it means those "reverse mapping" keys of number enums
const keys = parseKeys(raw);
const parsed = keys.map((key) => parseEnumItem(raw[key], key));
this[utils_1.KEYS] = keys;
Object.freeze(keys);
const items = [];
const meta = {};
this.meta = meta;
const named = {};
this.named = named;
keys.forEach((key, index) => {
const { value, label } = parsed[index];
const item = new enum_item_1.EnumItemClass(key, value, label, raw[key], options);
items.push(item);
this.push(item);
named[key] = item;
// Collect custom meta fields
const itemRaw = raw[key];
if (itemRaw && typeof itemRaw === 'object') {
Object.keys(itemRaw).forEach((k) => {
const metaKey = k;
if (metaKey !== 'key' && metaKey !== 'value' && metaKey !== 'label') {
if (meta[metaKey] == null) {
meta[metaKey] = [];
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const metaValue = itemRaw[metaKey];
if (metaValue != null) {
meta[metaKey].push(metaValue);
}
}
});
}
});
// Freeze meta arrays
Object.keys(meta).forEach((k) => {
Object.freeze(meta[k]);
});
// Generate values array
const values = parsed.map((item) => item.value);
this[utils_1.VALUES] = values;
Object.freeze(values);
// Generate labels array
Object.defineProperty(this, 'labels', {
get: function () {
// Cannot save to static array because labels may be localized contents
// Should not use `items` in the closure because the getter function cannot be fully serialized
return Array.from(this).map((item) => item.label);
},
enumerable: true,
configurable: false,
});
this._runtimeError = undefined;
Object.defineProperty(this, '_runtimeError', {
value: function (name) {
return `The ${name} property of the enumeration is only allowed to be used to declare the ts type, and cannot be accessed at runtime! Please use the typeof operator in the ts type, for example: typeof Week.${name}`;
},
writable: false,
enumerable: false,
configurable: false,
});
}
[Symbol.hasInstance](instance) {
// intentionally use == to support both number and string format value
return this.some(
// eslint-disable-next-line eqeqeq
(i) => instance == i.value || instance === i.key);
}
label(keyOrValue) {
var _a, _b;
// Find by value, then try key
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return (_b = ((_a = this.find((i) => i.value === keyOrValue)) !== null && _a !== void 0 ? _a : this.find((i) => i.key === keyOrValue))) === null || _b === void 0 ? void 0 : _b.label;
}
key(value) {
var _a;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return (_a = this.find((i) => i.value === value)) === null || _a === void 0 ? void 0 : _a.key;
}
raw(keyOrValue) {
if (keyOrValue == null) {
// Return the original initialization object
return this.__raw__;
}
else {
// Find by key
if (Object.keys(this.__raw__).some((k) => k === keyOrValue)) {
return this.__raw__[keyOrValue];
}
// Find by value
const itemByValue = this.find((i) => i.value === keyOrValue);
if (itemByValue) {
return itemByValue.raw;
}
return undefined;
}
}
has(keyOrValue) {
return this.some((i) => i.value === keyOrValue || i.key === keyOrValue);
}
findBy(field, value) {
return this.find((item) => {
var _a, _b;
if (field === 'key' || field === 'value') {
return item[field] === value;
}
else if (field === 'label') {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return ((_a = item.raw) === null || _a === void 0 ? void 0 : _a.label) === value || item.label === value;
}
else {
// For other fields, use the raw object to find
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return ((_b = item.raw) === null || _b === void 0 ? void 0 : _b[field]) === value;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
});
}
toList(config) {
const { valueField = 'value', labelField = 'label', extra } = config !== null && config !== void 0 ? config : {};
return Array.from(this).map((item) => {
const valueFieldName = typeof valueField === 'function' ? valueField(item) : valueField;
const labelFieldName = typeof labelField === 'function' ? labelField(item) : labelField;
const extraData = extra ? extra(item) : {};
const listItem = Object.assign({ [valueFieldName]: item.value, [labelFieldName]: item.label }, extraData);
return listItem;
});
}
toMap(config) {
if (!config) {
return this.reduce((prev, cur) => {
prev[cur.value] = cur.label;
return prev;
},
// eslint-disable-next-line @typescript-eslint/no-explicit-any
{});
}
const { keySelector = 'value', valueSelector = 'label' } = config;
return this.reduce((prev, cur) => {
let key;
if (typeof keySelector === 'function') {
key = keySelector(cur);
}
else {
key = cur[keySelector];
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let value;
if (typeof valueSelector === 'function') {
value = valueSelector(cur);
}
else {
value = cur[valueSelector];
}
prev[key] = value;
return prev;
}, {});
}
/** Stub method, only for typing usages, not for runtime calling */
get valueType() {
throw new Error(this._runtimeError('valueType'));
}
/** Stub method, only for typing usages, not for runtime calling */
get keyType() {
throw new Error(this._runtimeError('keyType'));
}
/** Stub method, only for typing usages, not for runtime calling */
get rawType() {
throw new Error(this._runtimeError('rawType'));
}
}
exports.EnumItemsArray = EnumItemsArray;
function parseKeys(raw) {
return Object.keys(raw).filter((k) => { var _a; return !(/^-?\d+$/.test(k) && k === `${(_a = raw[raw[k]]) !== null && _a !== void 0 ? _a : ''}`); });
}
function parseEnumItem(init, key) {
var _a, _b;
let value;
let label;
if (init != null) {
if (typeof init === 'number' || typeof init === 'string' || typeof init === 'symbol') {
value = init;
label = key;
}
else if (typeof init === 'object') {
// Initialize using object
if (Object.prototype.toString.call(init) === '[object Object]') {
if ('value' in init && Object.keys(init).some((k) => k === 'value')) {
// type of {value, label}
value = ((_a = init.value) !== null && _a !== void 0 ? _a : key);
if ('label' in init && Object.keys(init).some((k) => k === 'label')) {
label = init.label;
}
else {
label = key;
}
}
else if ('label' in init && Object.keys(init).some((k) => k === 'label')) {
// typeof {label}
value = key;
label = (_b = init.label) !== null && _b !== void 0 ? _b : key;
}
else {
// {} empty object
value = key;
label = key;
}
}
else {
// Probably Date, RegExp and other primitive types
value = init;
label = key;
}
}
else {
throw new Error(`Invalid enum item: ${JSON.stringify(init)}`);
}
}
else {
value = key;
label = key;
}
return { value, label };
}
//# sourceMappingURL=enum-items.js.map