How to merge two enums in TypeScript

前端 未结 6 1642
無奈伤痛
無奈伤痛 2021-01-01 10:30

Suppose I have two enums as described below in Typescript, then How do I merge them

enum Mammals {
    Humans,
    Bats,
    Dolphins
}

enum Reptiles {
             


        
6条回答
  •  醉梦人生
    2021-01-01 11:07

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

提交回复
热议问题