网站搜索

如何创建 TypeScript 枚举类型,使多个值映射到同一个键


我正在尝试将代码库中使用字符串类型的部分转换为类似枚举的类型,使用 TypeScript。问题是,在某些地方字符串值略有不同。与其创建两个几乎相同的类似枚举的类型,是否有办法创建一个单一的类型,让多个值匹配到相同的键?

我的尝试看起来像这样(但不起作用):

const STATUS = {
    CREATED: "CREATED",
    PICKED_UP: "PICKED UP" | "PICKED_UP",
    DELIVERED: "DELIVERED" | "DEL"
} as const
type ObjectValues<T> = T[keyof T];
type Status = ObjectValues<typeof STATUS>;

我正在寻找一个可以做到这一点的解决方案:

console.log(STATUS.PICKED_UP === "PICKED UP" && STATUS.PICKED_UP === "PICKED_UP") //should be true
doThingWithStatus("PICKED_UP") //should be ok, assuming function expects a type Status input

谢谢大家!

答案#1

如前所述,不,你所要求的是不可能的。你肯定不能让 同时 STATUS.PICKED_UP === "PICKED UP" 为真 并且 STATUS.PICKED_UP === "PICKED_UP" 为真,当 "PICKED UP" === "PICKED_UP" 为 假 时。STATUS.PICKED_UP 是某个值,这个值可以是其中某个字符串,也可以都不是,但它不能 同时 是这两个字符串。(这大概也是你在写像 "PICKED UP" | "PICKED_UP" 这样的 值 时遇到麻烦的原因。无论如何,那是按位 OR 运算符,对字符串没有作用。而且没有任何运算符可以把两个字符串合并成一个字符串,并使其 somehow 等于 两者。)


相反,看起来你应该重构,使你将 STATUS 的成员映射到字符串的 集合。例如,你可以使用数组,如

const STATUS = {
  CREATED: ["CREATED"],
  PICKED_UP: ["PICKED UP", "PICKED_UP"],
  DELIVERED: ["DELIVERED", "DEL"]
} as const;

那么 Status 是 ObjectValues<typeof STATUS> 的 元素 的并集:

type Status = ObjectValues<typeof STATUS>[number];
// type Status = "CREATED" | "PICKED UP" | "PICKED_UP" | "DELIVERED" | "DEL"

而你的检查将是 STATUS 的成员是否 includes 它集合中的某个字符串:

console.log(
  STATUS.PICKED_UP.includes("PICKED UP") &&
  STATUS.PICKED_UP.includes("PICKED_UP")
); // true

你不需要使用数组;你可以使用Set,或者使用字符串作为键的对象。


你甚至可以只是反转你的 STATUS 对象,使键和值互换,因为对象允许不同的 键 拥有相同的 值,但反过来不行(我也没看到你建议,例如 "DEL" 同时出现在 STATUS.DELIVERED 和比如 STATUS.DELETED 中。可以推测,你考虑的是多对一或一对多的关系,而不是多对多):

const STATUS_REV = {
  CREATED: "CREATED",
  PICKED_UP: "PICKED_UP",
  "PICKED UP": "PICKED_UP",
  DELIVERED: "DELIVERED",
  DEL: "DELIVERED"
} as const

然后 Status 是 STATUS_REV 的 键 的并集

type Status = keyof typeof STATUS_REV;
// type Status = "CREATED" | "PICKED UP" | "PICKED_UP" | "DELIVERED" | "DEL"

而你的检查将是前一个键是否为 STATUS_REV 的 "PICKED UP" 或 "PICKED_UP" 属性的 值:

console.log(
  "PICKED_UP" === STATUS_REV["PICKED UP"] && 
  "PICKED_UP" === STATUS_REV["PICKED_UP"]
); // true

代码的 Playground 链接