Merge TypeScript Enums
Tags: typescript, enum, programming, javascript
Published: 2022-09-18 11:33:41

There is no native support for merging 2 or more enums in typescript but its possible with a combination of [type aliases](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#type-aliases) and [enums](https://www.typescriptlang.org/docs/handbook/enums.html#handbook-content).

## Merge Enums

- Let suppose, you have 2 string based enums:
``` typescript
// enum 1
enum ManagedUserStatus {
  M_STATUS1 = 'M_STATUS1',
  M_STATUS2 = 'M_STATUS2',
  M_STATUS3 = 'M_STATUS3',
}

// enum 2
enum SystemUserStatus {
  Sys_STATUS1 = 'Sys_STATUS1',
  Sys_STATUS2 = 'Sys_STATUS2',
  Sys_STATUS3 = 'Sys_STATUS3',
}
```

- Convert the enums into a single javascript object, lets call it _**enum object**_. Utilize the enum object in your codebase as a regular enum. This object has the union of all enums combined.
``` typescript
// same enum like values
export const UserStatuses = { ...ManagedUserStatus,
 ...SystemUserStatus }

// OR

// Kind of nested enum approach
export const UserStatuses = { ManagedUserStatus,
 SystemUserStatus }
```

- Create a type from the enum object. Use this type as the regular enum type in your codebase.  

> Name of this type alias can be same as the enum object or anything else.
``` typescript
// 1. export as an object type
export type UserStatus = typeof UserStatuses;

// OR

// 2. export only the keys
export type UserStatus = keyof typeof UserStatuses;

// OR

// 3. export as a uinon type
export type UserStatus = ManagedUserStatus | SystemUserStatus;
```

This way, you can merge 2 or more enums and simulate the same enum like behavior in your codebase.

### Sample usage
Lets imagine a method in your code which utilizes our newly created enum.
``` typescript
function confirmUser(status: UserStatus){
    if(status === UserStatuses.M_STATUS2){
        return false;
    }
}
```

_____
## Merge Issues
There are some limitations and issues with this kind of merge and you have to be aware of them before merging your enums.
#### Numbered Enums
With numbered enums, you will have duplicate values in the enum object.
``` typescript
enum ManagedUserStatus {
  M_STATUS1,    // 0
  M_STATUS2,    // 1
  M_STATUS3     // 2
}

// enum 2
enum SystemUserStatus {
  Sys_STATUS1,  // 0
  Sys_STATUS2,  // 1
  Sys_STATUS3   // 2
}

const UserStatuses = { ...ManagedUserStatus,
...SystemUserStatus };

type UserStatuses = typeof UserStatuses;
```
Make sure that, if you need unique values then assign proper initializers to your enums like below:
``` typescript
enum ManagedUserStatus {
  M_STATUS1,    // 0
  M_STATUS2,    // 1
  M_STATUS3     // 2
}

// enum 2
enum SystemUserStatus {
  Sys_STATUS1 = 6,  // 6
  Sys_STATUS2,      // 7
  Sys_STATUS3       // 8
}
```
#### Duplicate Keys
If your enums have duplicate keys then that key in the final enum object has over-written value. See below exmple
``` typescript
enum ManagedUserStatus {
  M_STATUS1,    // 0
  M_STATUS2,    // 1
  M_STATUS3     // 2
}

// enum 2
enum SystemUserStatus {
  Sys_STATUS1,  // 0
  M_STATUS2 = 'CONFIRMED',
  Sys_STATUS3 = 'UNCONFIRMED'
}

const UserStatuses = { ...ManagedUserStatus, 
...SystemUserStatus };

type UserStatuses = typeof UserStatuses;

const m_status2_value = UserStatuses.M_STATUS2; //  'CONFIRMED'
```
-----
## Nested Enums
If you plan to use the nested enums for the enum object then maybe you can ignore above issues because there is no actual merging process in that case. only key based enums are exported out of the enum object. See below
``` typescript
enum ManagedUserStatus {
  M_STATUS1,    // 0
  M_STATUS2,    // 1
  M_STATUS3     // 2
}

// enum 2
enum SystemUserStatus {
  Sys_STATUS1,  // 0
  M_STATUS2 = 'CONFIRMED',
  Sys_STATUS3 = 'UNCONFIRMED'
}

const UserStatuses = { ManagedUserStatus, 
SystemUserStatus };

type UserStatuses = typeof UserStatuses;

// no effect of duplicate keys
const mStatus2_one = UserStatuses.ManagedUserStatus. M_STATUS2; //  '1'
const mStatus2_two = UserStatuses.SystemUserStatus. M_STATUS2; //  'CONFIRMED'

// no effect of numbered values
const sysStatus1 = UserStatuses.SystemUserStatus. Sys_STATUS1; //  '0'
```  
happy learning! 👊