向枚举添加函数

Add functions to an Enum

是否可以在 TypeScript 中向枚举类型添加函数?

例如:

enum Mode {
    landscape,
    portrait,

    // the dream...
    toString() { console.log(this); } 
}

或:

class ModeExtension {
    public toString = () => console.log(this);
}

enum Mode extends ModeExtension {
    landscape,
    portrait,
}

当然,toString() 函数会包含类似 switch 的内容,但用例会按照以下方式进行:

class Device {
    constructor(public mode:Mode) {
        console.log(this.mode.toString());
    }
}

我理解为什么扩展 enum 可能是一件奇怪的事情,只是想知道它是否可能。

您可以拥有一个独立于 Enum 的 class 并使用它来获取您想要的东西,或者您可以将命名空间合并到 Enum 中并在看起来像同一个地方的地方获取它.

模式实用程序Class

所以这并不是您想要的,但这允许您使用静态方法封装 "Mode to string" 行为。

class ModeUtil {
    public static toString(mode: Mode) {
        return Mode[mode];
    }
}

你可以这样使用它:

const mode = Mode.portrait;
const x = ModeUtil.toString(mode);
console.log(x);

模式Enum/Namespace合并

您可以将命名空间与 Enum 合并,以使用其他方法创建看起来像 Enum 的内容:

enum Mode {
    X,
    Y
}

namespace Mode {
    export function toString(mode: Mode): string {
        return Mode[mode];
    }

    export function parse(mode: string): Mode {
        return Mode[mode];
    }
}

const mode = Mode.X;

const str = Mode.toString(mode);
alert(str);

const m = Mode.parse(str);
alert(m);

您可以使用方括号获取非常量枚举的字符串值:

class Device {
    constructor(public mode:Mode) {
        console.log(Mode[this.mode]);
    }
}

您还可以将一些特定于枚举的实用程序函数放入枚举中,但这就像静态 class 成员一样:

enum Mode {
    landscape,
    portrait
}

namespace Mode {
    export function doSomething(mode:Mode) {
        // your code here
    }
}

将您的 enum 转换为枚举模式。我发现这对于许多语言来说通常是一种更好的做法,否则您会限制您的类型的封装选项。趋势是 switch 枚举值,实际上任何依赖于特定枚举值的数据或功能都应该进入枚举的每个实例。我添加了更多代码来演示。

如果您特别依赖底层枚举值,这可能不起作用。在这种情况下,您需要为旧值添加一个成员并将需要它的地方转换为使用新的 属性.

class Mode {
   public static landscape = new Mode(1920, 1080);
   public static portrait = new Mode(1080, 1920);

   public get Width(): number { return this.mWidth; }
   public get Height(): number { return this.mHeight; }

   // private constructor if possible in a future version of TS
   constructor(
      private mWidth: number,
      private mHeight: number
   ) {
   }

   public GetAspectRatio() {
      return this.mWidth / this.mHeight;
   }
}

可以通过私有构造函数和静态获取 return object

来生成枚举
export class HomeSlideEnum{

  public static get friendList(): HomeSlideEnum {

    return new HomeSlideEnum(0, "friendList");

  }
  public static getByOrdinal(ordinal){
    switch(ordinal){
      case 0:

        return HomeSlideEnum.friendList;
    }
  }

  public ordinal:number;
  public key:string;
  private constructor(ordinal, key){

    this.ordinal = ordinal;
    this.key = key;
  }


  public getTitle(){
    switch(this.ordinal){
      case 0:

        return "Friend List"
      default :
        return "DChat"
    }
  }

}

以后可以这样用

HomeSlideEnum.friendList.getTitle();

Fenton 解决方案的补充。 如果你想在另一个 class 中使用这个枚举器,你需要导出枚举和命名空间。它看起来像这样:

export enum Mode {
    landscape,
    portrait
}

export namespace Mode {
    export function toString(mode: Mode): string {
        return Mode[mode];
    }
}

然后您只需将 mode.enum.ts 文件导入 class 并使用它。

扩展枚举Class

我一直很喜欢 associated types in Swift 并且我一直在寻求扩展 Typescript 的枚举基本功能。这种方法背后的想法是在我们向枚举条目添加更多属性的同时保留 Typescript 枚举。提议的 class 旨在将对象与枚举条目相关联,而基本枚举结构保持不变。

如果您正在寻找一种方法来保留 vanilla Typescript 枚举,同时可以向每个条目添加更多属性,这种方法可能会有所帮助。

输入

enum testClass {
    foo = "bar",
    anotherFooBar = "barbarbar"
}

输出

{
  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'
    }
  ]
}

实施

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)
    }
}

用法

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."
    }
})

优势

  • 无需重构,保持基本枚举结构不变。
  • 您会获得每个枚举条目的代码建议(vs 代码)。
  • 如果您在声明枚举描述符(即关联类型)时出现拼写错误,则会出现错误。
  • 获得关联类型的好处并掌握 OOP 范例。
  • 扩展枚举条目是可选的,如果您不这样做,class 会为其生成默认条目。

缺点

这个 class 不支持带有数字键的枚举(这应该不会令人讨厌,因为我们通常使用枚举来提高人类的可读性,而我们几乎不使用数字作为枚举键)

当您为键分配数值时;打字稿 double-binds 枚举 key-values。为防止重复条目,此 class 仅考虑字符串键。