3 ways to merge two or more Enums in TypeScript

Updated: January 8, 2024 By: Guest Contributor Post a comment

Introduction

TypeScript provides several ways to work with enumerations (enums), which are a way to define a set of named constants. Sometimes, it might be necessary to merge two or more enums into one. In this article, we’ll explore various strategies to merge enums in TypeScript, each with its own benefits and potential drawbacks.

Solution 1: Union of Enums

The simple way to merge two enums is to create a union type that combines the enums. It’s an easy-to-understand approach and less verbose. However, the values don’t get merged but rather create a new type that encompasses all possible values.

Steps:

  1. Define the enumerations you wish to merge.
  2. Create a type that is the union of these enumerations.
  3. Use the union type wherever you need a merged enum.

Example:

enum Fruit { Apple = 'Apple', Banana = 'Banana' }
enum Color { Red = 'Red', Yellow = 'Yellow' }
type MergedEnum = Fruit | Color;
const myEnumValue: MergedEnum = Fruit.Apple;
console.log(myEnumValue);

As no real merging is happening, there is no significant performance impact. It’s more about type-checking at compile time.

Pros:

  • Simple and quick to implement
  • Low complexity

Cons: Enums remain separate and are not merged as a single construct.

Solution 2: Merging Enums with Namespaces

A more sophisticated approach involves merging enums using TypeScript’s namespace feature. This creates a true merged enum where the resulting type actually contains the members of both enums. You need to ensure compatibility of the enums because TypeScript does not support enums with overlapping value sets for merging.

Steps:

  1. Define the first enum normally.
  2. Create a namespace with the same name as the enum and define the second enum inside it.
  3. The TypeScript compiler will automatically merge the namespace with the enum, resulting in a single, merged enum.

Example:

enum Transport { Car = 'Car', Train = 'Train' }
namespace Transport {
    export enum Weather { Sunny = 'Sunny', Rainy = 'Rainy' }
}
console.log(Transport.Car);
console.log(Transport.Weather.Sunny);

The compiler handles the merging during compilation. The runtime performance is the same as for regular enums.

Pros:

  • Creates a single construct
  • Type safety is maintained with enum values
  • Grouping logically similar enums together

Cons:

  • May lead to confusion if not well documented
  • Cannot merge enums with overlapping values
  • Limited to TypeScript, as namespacing does not exist in JavaScript

Solution 3: Merging Using a Helper Function

Another approach to merge enums is to use a custom helper function that combines the keys and values from both enums into a new enum-like object. This approach can merge enums with overlapping values by either overwriting them or throwing an error.

Steps:

  1. Define the enums you want to merge.
  2. Write a helper function that takes enums as arguments and merges them.
  3. Create a merged enum by calling the helper function with the desired enums.

Example:

enum Directions { North = 'N', South = 'S' }
enum Commands { Start = 'Go', Stop = 'Halt' }
function mergeEnums(...enums: any[]): object {
    const merged = {};
    for (const currentEnum of enums) {
        for (const key in currentEnum) {
            if (Object.prototype.hasOwnProperty.call(currentEnum, key)) {
                const value = currentEnum[key];
                if (merged.hasOwnProperty(value)) {
                    throw new Error('Duplicate value detected: ' + value);
                }
                merged[key] = value;
            }
        }
    }
    return merged;
}
const MergedEnum = mergeEnums(Directions, Commands);
console.log(MergedEnum.North);
console.log(MergedEnum.Start);

This method involves creating a new object at runtime, which can have slight performance implications depending on the size of the enums.

Pros:

  • Can handle enums with overlapping values
  • Flexible and customizable
  • Merges into a true JavaScript object

Cons:

  • Not using the enum type directly but rather creating a new object
  • Requires extra runtime code and overhead
  • Manually written function which needs maintenance and testing

Conclusion

Choosing the right approach to merge enums in TypeScript depends on the specific requirements of the project. If maintaining enum constructs after merging is not crucial and there are no overlapping values, simply using union types can be a quick and easy solution. When the goal is to have a single enum structure that groups logically similar enums together or requires preserving enum semantics, merging with namespaces can be appropriate. Finally, using a custom helper function might be necessary when dealing with complex scenarios or when you need complete control over the merging process, albeit with some performance considerations. Understanding the pros and cons of each approach is key to making the best decision for merging enums in a TypeScript application.