How to merge two enums in TypeScript

Problems with the merge:

  • same values => values are overwritten
  • same keys => keys are overwritten

  • ❌ Enums with same values (=> values are overwritten)

enum AA1 {
  aKey, // = 0
  bKey // = 1
}
enum BB1 {
  cKey, // = 0
  dKey // = 1
}
  • ❌ Enums with the same keys (=> keys are overwritten)
enum AA2 {
  aKey = 1
}
enum BB2 {
  aKey = 2
}
  • ✅ Good
enum AA3 {
  aKey, // = 0
  bKey // = 1
}
enum BB3 {
  cKey = 2,
  dKey // = 3
}
  • ✅ Also Good
enum AA4 {
  aKey = 'Hello',
  bKey = 0,
  cKey // = 1
}
enum BB4 {
  dKey = 2,
  eKey = 'Hello',
  fKey = 'World'
}

Note:
aKey = 'Hello' and eKey = 'Hello' work because the enum with a string value doesn’t has this value as key

// For aKey = 'Hello', key is working
type aa4aKey = AA4.aKey; // = AA4.aKey
// value is not.
type aa4aValue = AA4.Hello; // ❌ Namespace 'AA4' has no exported member 'Hello'
type aa4aValue2 = AA4['Hello']; // ❌ Property 'Hello' does not exist on type 'AA4'

console.log(AA4); // { 0: 'bKey', 1: 'cKey', aKey: 'Hello', bKey: 0, cKey: 1 }
console.log(BB4); // { 2: 'dKey', dKey: 2, eKey: 'Hello', fKey: 'World' }

The merge

  • ❌ using union types
type AABB1 = AA4 | BB4; // = AA4 | BB4
type AABB1key = AABB1['aKey']; // = never
type AABB1key2 = AABB1.aKey; // ❌ 'AABB1' only refers to a type, but is being used as a namespace here. ts(2702)
  • ❌ using intersection types
type AABB1 = AA4 & BB4; // = never
type AABB1key = AABB1['aKey']; // = never
  • ✅ using intersection types with typeof
type AABB2 = (typeof AA4) & (typeof BB4); // = typeof AA4 & typeof BB4
type AABB2key = AABB2['aKey']; // = AA4.aKey
  • ✅ using js copy
const aabb1 = { ...AA4, ...BB4 };
const aabb2 = Object.assign({}, AA4, BB4); // also work
// aabb1 = {
// 0: 'bKey',
// 1: 'cKey',
// 2: 'dKey',
// aKey: 'Hello',
// bKey: 0,
// cKey: 1,
// dKey: 2,
// eKey: 'Hello',
// fKey: 'World' }
  • ✅ using typeof with a js copy
const aabb = { ...AA4, ...BB4 };
type TypeofAABB = typeof aabb;
// type TypeofAABB = {
// [x: number]: string;
// dKey: BB4.dKey;
// eKey: BB4.eKey;
// fKey: BB4.fKey;
// aKey: AA4.aKey;
// bKey: AA4.bKey;
// cKey: AA4.cKey;
// };

Tip: you can use the same name for a type and a value

const merged = { ...AA4, ...BB4 };
type merged = typeof merged;

const aValue = merged.aKey;
type aType = merged['aKey'];

Your case

If you want to merge your 2 enums you have ~3 choices:

1. Using string enums

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;

2. Using unique numbers

enum Mammals {
  Humans = 0,
  Bats,
  Dolphins
}

enum Reptiles {
  Snakes = 2,
  Alligators,
  Lizards
}

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

3. Using nested enums

enum Mammals {
  Humans,
  Bats,
  Dolphins
}

enum Reptiles {
  Snakes,
  Alligators,
  Lizards
}

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

const bats = Animals.Mammals.Bats; // = 1
const alligators = Animals.Reptiles.Alligators; // = 1

Note: you can also merge the nested enums with the following code. Take care to NOT have duplicated values if you do that!

type Animal = {
  [K in keyof Animals]: {
    [K2 in keyof Animals[K]]: Animals[K][K2]
  }[keyof Animals[K]]
}[keyof Animals];

const animal: Animal = 0 as any;

switch (animal) {
  case Animals.Mammals.Bats:
  case Animals.Mammals.Dolphins:
  case Animals.Mammals.Humans:
  case Animals.Reptiles.Alligators:
  case Animals.Reptiles.Lizards:
  case Animals.Reptiles.Snakes:
    break;
  default: {
    const invalid: never = animal; // no error
  }
}

Leave a Comment

Hata!: SQLSTATE[HY000] [1045] Access denied for user 'divattrend_liink'@'localhost' (using password: YES)