如何在Typescript中防止枚举值冲突

jexiocij  于 2023-05-30  发布在  TypeScript
关注(0)|答案(1)|浏览(138)

我的应用程序中有大量的枚举,其中一些枚举具有相同的值(尽管来自不同的枚举类型)。我还有一个泛型函数,它接受这些枚举中的任何一个,并尝试基于枚举类型和值来描述/处理。问题是,如果键相同,则枚举值会重复。
有没有办法解决这个问题?我在下面举例说明了这个问题:

enum Car {
  bmw = "bmw",
  ferrari = "ferrari"
}

enum Brand {
  bmw = "bmw",
  ferrari = "ferrari"
}

function describeIt(it: string) {
  switch (it) {
    case Car.bmw:
      console.log("It's a BMW car");
      break
    case Brand.bmw:
      console.log("It's the BMW brand");
      break
    case Car.ferrari:
      console.log("It's a Ferrari car");
      break
    case Brand.ferrari:
      console.log("It's the Ferrari brand");
      break
  }
}

describeIt(Car.bmw)   // Got: It's a BMW car, correct!
describeIt(Brand.bmw) // Got: It's a BMW car, expected: It's the BMW brand

在Typescript Playground试试看。
如果我问“如何在所有枚举值中生成一个唯一的值?”所以我问的是“如何防止枚举值冲突?”“这是这里的核心问题。

如果可能的话,我想保持简单的语法,我知道我可以像这样前置枚举类型,例如:

enum Brand {
  bmw = "Brand.bmw",
  ferrari = "Brand.ferrari"
}

但这不是我想要的,我想要一个来自枚举类型本身的解决方案。
我试着搜索了很多问题,但没有一个能解决这个特定的问题。

vh0rcniy

vh0rcniy1#

switch语句根据strict equality比较算法(参考:MDN、ECMAScript规范)。
因此,相同字符串的字符串枚举值将始终比较为相等(即使在不同的枚举中)-并且具有相同字符串值的第一个大小写将始终匹配。
为了区分值并仍然使用“枚举”习惯用法,你可以使用一个对象,它的值永远不会被比较为相等(例如对象,符号等)-TypeScript手册实际上建议这种对象模式:

对象vs枚举

在现代TypeScript中,当一个as const对象就足够了时,你可能不需要枚举:
...
支持这种格式而不是TypeScript的enum的最大论点是,它使您的代码库与JavaScript的状态保持一致,并且when/if枚举被添加到JavaScript中,然后您可以移动到其他语法。
下面是一个使用符号值的示例:
TSPlayground

type Values<T> = T[keyof T];

const Car = {
  Bmw: Symbol("bmw"),
  Ferrari: Symbol("ferrari"),
} as const;

type Car = Values<typeof Car>;

const Brand = {
  Bmw: Symbol("bmw"),
  Ferrari: Symbol("ferrari"),
} as const;

type Brand = Values<typeof Brand>;

function describeIt(it: Car | Brand): void {
  switch (it) {
    case Car.Bmw:
      console.log("It's a BMW car");
      break;
    case Brand.Bmw:
      console.log("It's the BMW brand");
      break;
    case Car.Ferrari:
      console.log("It's a Ferrari car");
      break;
    case Brand.Ferrari:
      console.log("It's the Ferrari brand");
      break;
  }
}

describeIt(Car.Bmw); // "It's a BMW car"
describeIt(Brand.Bmw); // "It's the BMW brand"

console.log(Car.Bmw === Car.Bmw); // true
console.log(Car.Bmw === Brand.Bmw); // false

如果出于某种原因,您需要可以序列化为字符串的枚举值,您可以使用自定义的toStringtoJSON方法创建一个对象类型,如下所示:
TSPlayground

class UniqueString {
  constructor(public value: string) {}
  toString(): string { return this.value; }
  toJSON(): string { return this.value; }
}

type Values<T> = T[keyof T];

const Car = {
  Bmw: new UniqueString("bmw"),
  Ferrari: new UniqueString("ferrari"),
} as const;

type Car = Values<typeof Car>;

const Brand = {
  Bmw: new UniqueString("bmw"),
  Ferrari: new UniqueString("ferrari"),
} as const;

type Brand = Values<typeof Brand>;

// ...etc. (same as previous example)

console.log(String(Car.Bmw)); // "bmw"
console.log(JSON.stringify(Car.Bmw)); // '"bmw"'

相关问题