@ncoderz/superenum
Version:
Simple, typesafe enums in TypeScript, fully compatible with standard JavaScript
1 lines • 17.8 kB
Source Map (JSON)
{"version":3,"sources":["../src/index.ts","../src/superenum.ts"],"sourcesContent":["export {\n Enum,\n type EnumFunc,\n type EnumType,\n type FromKeyOptions,\n type FromValueOptions,\n type HasKeyOptions,\n type HasValueOptions,\n type KeyFromValueOptions,\n type Labels,\n} from './superenum';\n","/**\n * Create an enum type from an enum like object or array.\n */\nexport type EnumType<T> = T[keyof T];\n\n/**\n * Options for the {@link Superenum.fromValue} function\n */\nexport interface FromValueOptions {\n /**\n * Ignore case when validating the enum value\n */\n ignoreCase?: boolean;\n}\n\n/**\n * Options for the {@link Superenum.fromKey} function\n */\nexport interface FromKeyOptions {\n /**\n * Ignore case when getting the enum value from the key\n */\n ignoreCase?: boolean;\n}\n\n/**\n * Options for the {@link Superenum.keyFromValue} function\n */\nexport interface KeyFromValueOptions {\n /**\n * Ignore case when getting the enum key from the value\n */\n ignoreCase?: boolean;\n}\n\n/**\n * Options for the {@link Superenum.hasKey} function\n */\nexport interface HasKeyOptions {\n /**\n * Ignore case when getting the enum key from the value\n */\n ignoreCase?: boolean;\n}\n\n/**\n * Options for the {@link Superenum.hasValue} function\n */\nexport interface HasValueOptions {\n /**\n * Ignore case when getting the enum key from the value\n */\n ignoreCase?: boolean;\n}\n\n/**\n * i18n labels for enum values.\n */\nexport interface Labels {\n [key: string]: string;\n}\n\n/**\n * Array Enum declaration\n */\nexport type ArrayEnum<KV extends EnumKey> = ReadonlyArray<KV>;\n\n/**\n * Convert an ArrayEnum type to an ObjectEnum\n */\nexport type ArrayEnumToObjectEnum<T extends ReadonlyArray<string>> = {\n [K in T[number]]: K;\n};\n\nexport interface Superenum<\n K extends EnumKey = EnumKey,\n V extends EnumValue = EnumValue,\n T extends ObjectEnum<K, V> = ObjectEnum<K, V>,\n> {\n /**\n * Validate a possible enum value, returning the enum value if valid, otherwise undefined.\n *\n * Since an enum value is just the value, then all this function does is check to see if the value exists on the enum, and if\n * so returns it cast to the enum type, otherwise it returns undefined.\n *\n * Note: If the enum has duplicate values when lower-cased and\n * {@link FromValueOptions.ignoreCase} is true, the data returned when when values clash will be indeterminate.\n *\n * @param value - the enum value to validate\n * @param options - options for the function\n * @returns the enum value, or undefined if the value cannot be matched to the enum\n */\n fromValue(value: unknown | null | undefined, options?: FromValueOptions): EnumType<T> | undefined;\n\n /**\n * Get an enum value from its key, returning the value if key valid, otherwise undefined.\n *\n * Note: If the enum has duplicate keys when lower-cased and\n * {@link FromKeyOptions.ignoreCase} is true, the data returned when when keys clash will be indeterminate.\n *\n * @param key - the enum key to convert to enum value\n * @param options - options for the function\n * @returns the enum represented by the key, or undefined if the key cannot be matched to the enum\n */\n fromKey(\n key: EnumKey | number | null | undefined,\n options?: FromKeyOptions,\n ): EnumType<T> | undefined;\n\n /**\n * Get an enum key from its value, returning the key if value valid, otherwise undefined.\n *\n * Note: If the enum has duplicate values when lower-cased and\n * {@link FromValueOptions.ignoreCase} is true, the data returned when when values clash will be indeterminate.\n *\n * @param value - the enum value to convert to enum key\n * @param options - options for the function\n * @returns the enum key represented by the value, or undefined if the value cannot be matched to the enum\n */\n keyFromValue(\n value: unknown | null | undefined,\n options?: KeyFromValueOptions,\n ): string | undefined;\n\n /**\n * Check if an enum has a value, returning true if yes, otherwise false.\n *\n * Note: If the enum has duplicate values (or duplicate values when lower-cased if\n * {@link HasValueOptions.ignoreCase} is true), the data returned when when values clash will be indeterminate.\n *\n * @param value - the enum value to check\n * @param options - options for the function\n * @returns true if the enum has the value, otherwise false\n */\n hasValue(value: EnumType<T> | null | undefined, options?: HasValueOptions): boolean;\n\n /**\n * Check if an enum has a key, returning true if yes, otherwise false.\n *\n * Note: If the enum has duplicate keys when lower-cased and\n * {@link FromKeyOptions.ignoreCase} is true, the data returned when when keys clash will be indeterminate.\n *\n * @param key - the enum key to check\n * @param options - options for the function\n * @returns true if the enum has the key, otherwise false\n */\n hasKey(key: EnumKey | null | undefined, options?: HasKeyOptions): boolean;\n\n /**\n * Get an array of the enum values.\n *\n * @returns iterator over the enum values\n */\n values(): readonly EnumType<T>[];\n\n /**\n * Get an array of the enum keys.\n *\n * @returns iterator over the enum values\n */\n keys(): readonly ExtractEnumKey<K, V, T>[];\n\n /**\n * Get an array of the enum entries.\n *\n * @returns iterator over the enum values\n */\n entries(): readonly [ExtractEnumKey<K, V, T>, EnumType<T>][];\n\n /**\n * An iterator that iterates the enum values.\n *\n * @returns iterator over the enum values\n */\n [Symbol.iterator](): IterableIterator<EnumType<T>>;\n\n /**\n * Set i18n labels for all enum values.\n *\n * @param allLabels - an object containing i18n labels for each enum value\n */\n setAllLabels(allLabels: { [key: EnumValue]: Labels }): void;\n\n /**\n * Set i18n labels for a specific enum value.\n *\n * @param value - the enum value to set i18n labels for\n * @param labels - an object containing i18n labels for the enum value\n */\n setLabels(value: EnumType<T>, labels: Labels): void;\n\n /**\n * Get i18n labels for a specific enum value.\n *\n * @param value - the enum value to get i18n labels for\n * @returns an object containing i18n labels for the enum value\n */\n getLabels(value: EnumType<T>): Labels;\n\n /**\n * Get a label for a specific enum value in a specific locale.\n *\n * If no locale is provided, it will return the first label that was set.\n * If no label is found for the value, it will return the value as a string.\n *\n * @param value - the enum value to get the label for\n * @param locale - the locale to get the label for\n * @returns the label for the enum value in the specified locale\n */\n getLabel(value: EnumType<T>, locale?: string): string;\n}\n\ntype EnumKey = string;\ntype EnumValue = string | number;\ntype ExtractEnumKey<K extends EnumKey, V extends EnumValue, T extends ObjectEnum<K, V>> = keyof T;\n\ntype GenericEnum = Readonly<Record<EnumKey, EnumValue>>;\n\ntype ObjectEnum<K extends EnumKey, V extends EnumValue> = { [key in K]: V };\n\ninterface Cache {\n _keys: EnumKey[];\n _valueKeyMap: Map<EnumValue, EnumKey>;\n _keyValueMap?: Map<EnumKey, EnumValue>;\n _lcValueKeyMap?: Map<EnumValue, EnumKey>;\n _lcKeyValueMap?: Map<EnumKey, EnumValue>;\n _valueLabelMap?: Map<EnumValue, Labels>;\n _values?: EnumValue[];\n _entries?: [EnumKey, EnumValue][];\n}\n\nconst CACHED_ENUMS = new WeakMap<GenericEnum, Cache>();\n\n/**\n * Wraps an enum or enum-like object to provide methods for interacting with it.\n *\n * Uses a WeakMap and lazy instantiation to cache the enum's keys, values, and labels\n * for fast performance while keeping memory footprint small.\n *\n * @param enm - an enum or enum like object\n * @returns a Superenum object that provides methods to interact with the enum\n */\nfunction Enum<K extends string, V extends string | number, T extends ObjectEnum<K, V>>(\n enm: T,\n): Superenum<K, V, T> {\n let _cache: Cache = CACHED_ENUMS.get(enm) as Cache;\n\n // Get the enum as a generic object\n const enmAny = enm as unknown as Record<string, EnumValue>;\n\n if (!_cache) {\n // Get iteration keys from the enum object (ignore reverse mapped integers)\n const enmKeys: EnumKey[] = [];\n for (const key in enm) {\n const nkey = Number(key);\n const isReverseKey = !isNaN(nkey) && enmAny[enmAny[nkey]] === nkey;\n\n if (!isReverseKey) {\n enmKeys.push(key);\n }\n }\n\n const newCache: Cache = {\n _keys: enmKeys,\n _valueKeyMap: new Map(),\n } as Cache;\n\n // Fill keyValueMap and lcKeyValueMap, valueKeyMap and lcValueKeyMap\n for (const key of newCache._keys) {\n const value = enmAny[key];\n newCache._valueKeyMap.set(value, key);\n }\n\n CACHED_ENUMS.set(enm, newCache);\n _cache = newCache;\n }\n\n function createKeyValueMap(): Map<EnumKey, EnumValue> {\n const newMap = new Map<EnumKey, EnumValue>();\n for (const key of _cache._keys) {\n const value = enmAny[key] as EnumValue;\n newMap.set(key, value);\n }\n _cache._keyValueMap = newMap;\n return newMap;\n }\n\n function createLowerCaseValueKeyMap(): Map<EnumValue, EnumKey> {\n const newMap = new Map<EnumValue, EnumKey>();\n for (const key of _cache._keys) {\n const value = enmAny[key];\n const lcValue = typeof value === 'string' ? value.toLowerCase() : value;\n newMap.set(lcValue, key);\n }\n _cache._lcValueKeyMap = newMap;\n\n return newMap;\n }\n\n function createLowerCaseKeyValueMap(): Map<EnumKey, EnumValue> {\n const newMap = new Map<EnumKey, EnumValue>();\n for (const key of _cache._keys) {\n const value = enmAny[key];\n newMap.set(key.toLowerCase(), value as EnumValue);\n }\n _cache._lcKeyValueMap = newMap;\n return newMap;\n }\n\n function createValueLabelMap(): Map<EnumValue, Labels> {\n const newMap = new Map<EnumValue, Labels>();\n _cache._valueLabelMap = newMap;\n return newMap;\n }\n\n function createValues() {\n const keyValueMap = _cache._keyValueMap ?? createKeyValueMap();\n _cache._values = _cache._keys.map((k) => keyValueMap.get(k)!);\n return _cache._values;\n }\n\n function createEntries() {\n const keyValueMap = _cache._keyValueMap ?? createKeyValueMap();\n _cache._entries = _cache._keys.map((k) => [k, keyValueMap.get(k)!]);\n return _cache._entries;\n }\n\n function fromValue(value: EnumValue, options?: FromValueOptions) {\n if (options?.ignoreCase && typeof value === 'string') {\n const keyValueMap = _cache._keyValueMap ?? createKeyValueMap();\n const lcValueKeyMap = _cache._lcValueKeyMap ?? createLowerCaseValueKeyMap();\n const key = lcValueKeyMap.get(value.toLowerCase());\n if (!key) return undefined;\n return keyValueMap.get(key);\n }\n if (!_cache._valueKeyMap.has(value)) return undefined;\n return value;\n }\n\n function fromKey(key: EnumKey, options?: FromKeyOptions) {\n const keyValueMap = _cache._keyValueMap ?? createKeyValueMap();\n if (options?.ignoreCase && typeof key === 'string') {\n const lcKeyValueMap = _cache._lcKeyValueMap ?? createLowerCaseKeyValueMap();\n return lcKeyValueMap.get(key.toLowerCase());\n }\n return keyValueMap.get(`${key}`);\n }\n\n function keyFromValue(value: EnumValue, options?: KeyFromValueOptions) {\n if (options?.ignoreCase && typeof value === 'string') {\n const lcValueKeyMap = _cache._lcValueKeyMap ?? createLowerCaseValueKeyMap();\n return lcValueKeyMap.get(value.toLowerCase());\n }\n return _cache._valueKeyMap.get(value);\n }\n\n function hasKey(key: EnumKey, options?: HasKeyOptions) {\n return fromKey(key, options) != null;\n }\n\n function hasValue(value: EnumValue, options?: HasValueOptions) {\n return fromValue(value, options) != null;\n }\n\n function keys() {\n return _cache._keys;\n }\n\n function values() {\n return _cache._values ?? createValues();\n }\n\n function entries() {\n return _cache._entries ?? createEntries();\n }\n\n function* valueIterator() {\n for (const v of values()) {\n yield v;\n }\n }\n\n function setAllLabels(allLabels: { [key: EnumValue]: Labels }): void {\n const valueLabelMap = _cache._valueLabelMap ?? createValueLabelMap();\n for (const [v, labels] of Object.entries(allLabels)) {\n const value = fromValue(v);\n if (value != null) {\n valueLabelMap.set(value, labels);\n }\n }\n }\n\n function setLabels(value: EnumValue, labels: Labels): void {\n const valueLabelMap = _cache._valueLabelMap ?? createValueLabelMap();\n valueLabelMap.set(value, labels);\n }\n\n function getLabels(value: EnumValue): Labels {\n const valueLabelMap = _cache._valueLabelMap ?? createValueLabelMap();\n return valueLabelMap.get(value) ?? {};\n }\n\n function getLabel(value: EnumValue, locale?: string): string {\n const valueLabelMap = _cache._valueLabelMap ?? createValueLabelMap();\n const labels = valueLabelMap.get(value) ?? {};\n if (!locale) {\n for (const label of Object.values(labels)) {\n return label ?? `${value}`;\n }\n }\n return labels[locale as string] ?? `${value}`;\n }\n\n return {\n fromValue,\n fromKey,\n keyFromValue,\n hasKey,\n hasValue,\n keys: () => keys(),\n values: () => values(),\n entries: () => entries(),\n [Symbol.iterator]: valueIterator,\n setAllLabels,\n setLabels,\n getLabels,\n getLabel,\n } as unknown as Superenum<K, V, T>;\n}\n\nEnum.fromArray = <KV extends Readonly<EnumKey>, T extends ArrayEnum<KV>>(\n enumeration: T,\n): ArrayEnumToObjectEnum<T> => {\n let arr: ArrayEnum<KV> = enumeration;\n if (!Array.isArray(arr)) arr = [];\n\n const enm = arr.reduce((acc, v) => {\n acc[v] = v;\n return acc;\n }, {});\n\n return enm as ArrayEnumToObjectEnum<T>;\n};\n\nexport type EnumFunc = typeof Enum;\n\nexport { Enum };\n"],"mappings":"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","names":[]}