6

Then there is the code:

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


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

I know there are some solutions like

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

But type is different from enum, as it's not iterable!

QC CAI
  • 131
  • 1
  • 6

2 Answers2

5

An enum is simply an object at runtime. For example, your enum all gets transformed to this:

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

You can therefore use all like any normal JavaScript object. There are many different ways to pick/omit certain properties:

// 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'}

If you wanted to make the last example more type-safe, you could use this helper:

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'])

Or maybe this for omitting properties instead:

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'])

In TypeScript, types can have the same names as values, so if you wanted wanted to also be a type (just like if you had written out the enum), you could define a type wanted with Exclude or Pick (like in your example).

Playground link

Lauren Yim
  • 12,700
  • 2
  • 32
  • 59
2

I actually find a solution to merge enum

enum Mammals {
  Humans = 'Humans',
  Bats = 'Bats',
  Dolphins = 'Dolphins'
}

enum Reptiles {
  Snakes = 'Snakes',
  Alligators = 'Alligators',
  Lizards = 'Lizards'
}

export const Animals = { ...Mammals, ...Reptiles };
export type Animals = typeof Animals;

So you can import Animals act as enum.

QC CAI
  • 131
  • 1
  • 6