首页 > 解决方案 > 如何从另一个枚举声明子集枚举

问题描述

然后是代码:

enum all {
  a = 'a',
  b = 'b',
  c = 'c',
}


// what I want enum
enum wanted {
  c = 'c',
}

我知道有一些解决方案,例如

type wanted = Exclude<all, all.a | all.b> // type wanted = all.c

但是类型与枚举不同,因为它不可迭代!

标签: typescript

解决方案


枚举只是运行时的对象。例如,您的枚举all被转换为:

var all;
(function (all) {
    all["a"] = "a";
    all["b"] = "b";
    all["c"] = "c";
})(all || (all = {}));

// Essentially equivalent to
const all = {a: 'a', b: 'b', c: 'c'}

因此,您可以all像使用任何普通的 JavaScript 对象一样使用。有许多不同的方法可以选择/省略某些属性

// ES2015 Destructuring
const {a, b, ...wanted} = all

// ES2019 Object.fromEntries
const wanted = Object.fromEntries(
  Object.entries(all)
.filter(([key]) =>
  key === 'c'
 // or if you wanted to exclude instead
// !['a', 'b'].includes(key)
)
) as {c: 'c'}

如果你想让最后一个例子更安全,你可以使用这个助手:

const pick = <T extends Record<string, unknown>, K extends keyof T>(
  obj: T,
  keys: readonly K[]
): Pick<T, K> =>
  Object.fromEntries(
    Object.entries(obj).filter(([key]) =>
      (keys as readonly string[]).includes(key)
    )
  ) as Pick<T, K>

const wanted = pick(all, ['c'])

或者也许这是为了省略属性:

const omit = <T extends Record<string, unknown>, K extends keyof T>(
  obj: T,
  keys: readonly K[]
): Omit<T, K> =>
  Object.fromEntries(
    Object.entries(obj).filter(
      ([key]) => !(keys as readonly string[]).includes(key)
    )
  ) as Omit<T, K>

const wanted = omit(all, ['a', 'b'])

在 TypeScript 中,类型可以与值具有相同的名称,因此如果您wanted也想成为一个类型(就像您写出枚举一样),您可以wanted使用Excludeor定义一个类型Pick(就像在您的示例中一样)。

游乐场链接


推荐阅读