ExtendedEnum Class
I always loved the associated types in Swift and I was looking to extend Typescript's enum basic functionality. The idea behind this approach was to keep Typescript enums while we add more properties to an enum entry. The proposed class intends to associate an object with an enum entry while the basic enum structure stays the same.
If you are looking for a way to keep vanilla Typescript enums while you can add more properties to each entry, this approach might be helpful.
Input
enum testClass {
foo = "bar",
anotherFooBar = "barbarbar"
}
Output
{
entries: [
{
title: 'Title for Foo',
description: 'A simple description for entry foo...',
key: 'foo',
value: 'bar'
},
{
title: 'anotherFooBar',
description: 'Title here falls back to the key which is: anotherFooBar.',
key: 'anotherFooBar',
value: 'barbarbar'
}
]
}
Implementation
export class ExtendedEnum {
entries: ExtendedEnumEntry[] = []
/**
* Creates an instance of ExtendedEnum based on the given enum class and associated descriptors.
*
* @static
* @template T
* @param {T} enumCls
* @param {{ [key in keyof T]?: EnumEntryDescriptor }} descriptor
* @return {*} {ExtendedEnum}
* @memberof ExtendedEnum
*/
static from<T extends Object>(enumCls: T, descriptor: { [key in keyof T]?: EnumEntryDescriptor }): ExtendedEnum {
const result = new ExtendedEnum()
for (const anEnumKey of Object.keys(enumCls)) {
if (isNaN(+anEnumKey)) { // skip numerical keys generated by js.
const enumValue = enumCls[anEnumKey]
let enumKeyDesc = descriptor[anEnumKey] as EnumEntryDescriptor
if (!enumKeyDesc) {
enumKeyDesc = {
title: enumValue
}
}
result.entries.push(ExtendedEnumEntry.fromEnumEntryDescriptor(enumKeyDesc, anEnumKey, enumValue))
}
}
return result
}
}
export interface EnumEntryDescriptor {
title?: string
description?: string
}
export class ExtendedEnumEntry {
title?: string
description?: string
key: string
value: string | number
constructor(title: string = null, key: string, value: string | number, description?: string) {
this.title = title ?? key // if title is not provided fallback to key.
this.description = description
this.key = key
this.value = value
}
static fromEnumEntryDescriptor(e: EnumEntryDescriptor, key: string, value: string | number) {
return new ExtendedEnumEntry(e.title, key, value, e.description)
}
}
Usage
enum testClass {
foo = "bar",
anotherFooBar = "barbarbar"
}
const extendedTestClass = ExtendedEnum.from(testClass, {
foo: {
title: "Title for Foo",
description: "A simple description for entry foo..."
},
anotherFooBar: {
description: "Title here falls back to the key which is: anotherFooBar."
}
})
Advantages
- No refactor needed, keep the basic enum structures as is.
- You get code suggestions for each enum entry (vs code).
- You get an error if you have a typo in declaring the enum descriptors aka associated types.
- Get the benefit of associated types and stay on top of OOP paradigms.
- It is optional to extend an enum entry, if you don't, this class generates the default entry for it.
Disadvantages
This class does not support enums with numerical keys (which shouldn't be annoying, because we usually use enums for human readability and we barely use numbers for enum keys)
When you assign a numerical value to a key; Typescript double-binds the enum key-values. To prevent duplicate entries, this class only considers string keys.