【问题标题】:How to merge two enums in TypeScript如何在 TypeScript 中合并两个枚举
【发布时间】:2018-07-06 19:07:29
【问题描述】:

假设我有两个在 Typescript 中描述的枚举,那么我该如何合并它们

enum Mammals {
    Humans,
    Bats,
    Dolphins
}

enum Reptiles {
    Snakes,
    Alligators,
    Lizards
}

export default Mammals & Reptiles // For Illustration purpose, Consider both the Enums have been merged.

现在,当我在另一个文件中 import exported value 时,我应该能够访问这两个枚举中的值。

import animalTypes from "./animalTypes"

animalTypes.Humans //valid

animalTypes.Snakes // valid

如何在 TypeScript 中实现这样的功能?

【问题讨论】:

    标签: typescript types enums merge typescript2.0


    【解决方案1】:

    您需要对枚举值和正确的导出类型使用字符串 id:

    enum Mammals {
      Humans = 'humans',
      Bats = 'bats',
      Dolphins = 'dolphins',
    }
    
    enum Reptiles {
      Snakes = 'snakes',
      Alligators = 'alligators',
      Lizards = 'lizards',
    }
    
    export const Animals = { ...Mammals, ...Reptiles };
    type TAnimalsKeys = keyof typeof Animals;
    export type TAnimal = typeof Animals[TAnimalsKeys];
    

    【讨论】:

      【解决方案2】:

      试试这个枚举示例 ------

      枚举或枚举是 TypeScript 支持的新数据类型

      enum PrintMedia {
          Newspaper = 1,
          Newsletter,
          Magazine,
          Book
      }
      
      function getMedia(mediaName: string): PrintMedia {
          if (mediaName === 'Forbes' || mediaName === 'Outlook') {
              return PrintMedia.Magazine;
          }
       }
      
      let mediaType: PrintMedia = getMedia('Forbes');
      

      【讨论】:

        【解决方案3】:

        如果你想从消费方式上表现得像枚举,你仍然可以在 javascript 中使用合并对象。

        enum Mammals {
            Humans = 'Humans',
            Bats = 'Bats',
            Dolphins = 'Dolphins',
        }
        
        enum Reptiles {
          Snakes = 'Snakes',
          Alligators = 'Alligators',
          Lizards = 'Lizards',
        }
        
        const Animals = {
           ...Mammals,
           ...Reptiles,
        }
        
        type Animals = Mammals | Reptiles
        

        然后你可以使用 Animals.Snakes 或 Animals.Dolphins 并且两者都应该被正确键入并作为枚举工作

        【讨论】:

        • 没想到这个角度……会试试看。
        • 从@Porkishka 答案中添加type Animals = Mammals | Reptiles;,它可以工作并通过类型检查
        • 我现在要如何导出Animals
        • 如果您同时导出变量和类型,您应该没有任何问题
        【解决方案4】:

        标记一个(也许更好的)方法来做到这一点:

        export enum Fruit {
          COCONUT = "COCO",
          BANANA = "BANANA",
        }
        
        export enum Vegetable {
          BROCCOLI = "BROCCOLI",
        }
        
        export const Foods = {
          ...Fruit,
          ...Vegetable,
        };
        
        export type Food = keyof typeof Foods;
        

        确保您定义枚举的字符串不会发生冲突。 Food 是类型,Foods 是定义枚举的底层映射(通常 js 会为您创建)。这种方式很酷的地方在于:

        Foods[Food.BROCCOLI] 是字符串“BROCCOLI”,就像Fruit[Fruit.COCONUT] 是字符串“COCO”一样,编译器知道这些类型。

        因此,结合 FoodsFood 您将获得标准枚举行为。

        【讨论】:

          【解决方案5】:

          一个非常简单的解决方案,copied from here

          对于两组不同的枚举:

          enum WeatherType {
            CLOUDY,
            SUNNY,
            RAIN
          }
          
          enum WeatherType {
            CLOUDY = 0,
            SUNNY = 1,
            RAIN = 2
          }
          

          然后您可以使用: type MyMergedEnum = AnEnum & AnotherEnum;

          【讨论】:

            【解决方案6】:

            合并问题:

            • 相同的值 => 值被覆盖
            • 相同的键 => 键被覆盖

            • ❌ 具有相同值的枚举(=> 值被覆盖)

            enum AA1 {
              aKey, // = 0
              bKey // = 1
            }
            enum BB1 {
              cKey, // = 0
              dKey // = 1
            }
            
            • ❌ 具有相同键的枚举(=> 键被覆盖)
            enum AA2 {
              aKey = 1
            }
            enum BB2 {
              aKey = 2
            }
            
            • ✅ 好
            enum AA3 {
              aKey, // = 0
              bKey // = 1
            }
            enum BB3 {
              cKey = 2,
              dKey // = 3
            }
            
            • ✅也不错
            enum AA4 {
              aKey = 'Hello',
              bKey = 0,
              cKey // = 1
            }
            enum BB4 {
              dKey = 2,
              eKey = 'Hello',
              fKey = 'World'
            }
            

            注意: aKey = 'Hello'eKey = 'Hello' 有效,因为具有字符串值的枚举没有此值作为键

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

            合并

            • ❌ 使用联合类型
            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)
            
            • ❌使用交叉点类型
            type AABB1 = AA4 & BB4; // = never
            type AABB1key = AABB1['aKey']; // = never
            
            • ✅ 使用带有 typeof 的交集类型
            type AABB2 = (typeof AA4) & (typeof BB4); // = typeof AA4 & typeof BB4
            type AABB2key = AABB2['aKey']; // = AA4.aKey
            
            • ✅使用js复制
            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' }
            
            • ✅ 将 typeof 与 js 副本一起使用
            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;
            // };
            

            提示:类型和值可以使用相同的名称

            const merged = { ...AA4, ...BB4 };
            type merged = typeof merged;
            
            const aValue = merged.aKey;
            type aType = merged['aKey'];
            

            您的情况

            如果你想合并你的 2 个枚举,你有 ~3 个选择:

            1。使用字符串枚举

            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。使用唯一编号

            enum Mammals {
              Humans = 0,
              Bats,
              Dolphins
            }
            
            enum Reptiles {
              Snakes = 2,
              Alligators,
              Lizards
            }
            
            export const Animals = { ...Mammals, ...Reptiles };
            export type Animals = typeof Animals;
            

            3。使用嵌套枚举

            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
            

            注意:您还可以将嵌套枚举与以下代码合并。如果这样做,请注意不要有重复的值!

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

            【讨论】:

            • 多么全面的答案!
            • 我不能使用带有开关的嵌套枚举
            • @lolelo 您可以使用打字稿以一种有点骇人听闻的方式做到这一点。这是一个示例(也在编辑的答案中):pastiebin.com/5d681d2cde9cc
            • @ShashankVivek 基本上是复制/粘贴表情符号(符号部分)。它们是书面解释的绝佳资源:)
            • 例如 1 和 2,而不是 export type Animals = typeof Animals; 你应该做 export type Animals = Mammals | Reptiles;所以哺乳动物可以分配给动物
            【解决方案7】:

            枚举、接口和类型 - 合并枚举的有效解决方案

            这里令人困惑的是类型与值。

            • 如果您定义一个letconst 等),它将有一个值加上一些计算但未单独命名的类型。
            • 如果你定义了一个typeinterface,它会创建一个命名类型,但不会以任何方式在最终的JS中输出或考虑。只有在编写应用时才有帮助。
            • 如果你在 Typescript 中创建一个enum,它会创建一个你可以使用的静态类型名称​​加上一个输出到 JS 的真实对象你可以使用。

            来自 TS 手册:

            使用枚举很简单:只需将任何成员作为枚举本身的属性访问,并使用枚举的名称声明类型。

            所以,如果你Object.assign() 两个枚举,它将创建一个新的合并(对象),而不是一个新的命名类型。

            由于它不再是 enum,因此您失去了拥有值和命名类型的优势,但您仍然可以创建一个单独的类型名称作为解决方法。

            幸运的是,值和类型可以使用相同的名称,如果导出它们,TS 会同时导入它们。

            // This creates a merged enum, but not a type
            const Animals = Object.assign({}, Mammals, Reptiles);
            
            // Workaround: create a named type (typeof Animals won't work here!)
            type Animals = Mammals | Reptiles;
            

            TS playground link

            【讨论】:

              【解决方案8】:

              我认为正确的做法是定义一个新类型:

              enum Mammals {
                  Humans = 'Humans',
                  Bats = 'Bats',
                  Dolphins = 'Dolphins',
              }
              
              enum Reptiles {
                Snakes = 'Snakes',
                Alligators = 'Alligators',
                Lizards = 'Lizards',
              }
              
              type Animals = Mammals | Reptiles;
              

              【讨论】:

              • 这种方法的问题是,您不能从中选择任何值。错误:Animals 然后。 Animals only refers to a type, but is being used as a value here
              【解决方案9】:

              TypeScript 枚举不仅包含您定义的键,还包含数字逆,例如:

              Mammals.Humans === 0 && Mammals[0] === 'Humans'
              

              现在,如果您尝试合并它们——例如使用Object#assign——你最终会得到两个具有相同数值的键:

              const AnimalTypes = Object.assign({}, Mammals, Reptiles);
              console.log(AnimalTypes.Humans === AnimalTypes.Snakes) // true
              

              我想这不是你想要的。

              防止这种情况的一种方法是手动将值分配给枚举并确保它们不同:

              enum Mammals {
                  Humans = 0,
                  Bats = 1,
                  Dolphins = 2
              }
              
              enum Reptiles {
                  Snakes = 3,
                  Alligators = 4,
                  Lizards = 5
              }
              

              或不那么明确但在其他方面等效:

              enum Mammals {
                  Humans,
                  Bats,
                  Dolphins
              }
              
              enum Reptiles {
                  Snakes = 3,
                  Alligators,
                  Lizards
              }
              

              无论如何,只要确保合并的枚举具有不同的键/值集,就可以将它们与Object#assign 合并。

              Playground Demo

              【讨论】:

              • 合并似乎有效,但我不能在界面中使用它。下面的游乐场演示。 @陶
              • 我想问题是合并的对象不再是 TypeScript 的枚举,尽管它的行为相同。作为一种解决方法,您可以编写typeof AnimalTypes.Bats,它将解析为Mammals.Bats。也许其他人知道更好的方法。
              猜你喜欢
              • 2023-02-17
              • 1970-01-01
              • 1970-01-01
              • 2021-10-10
              • 2020-08-10
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-02-09
              相关资源
              最近更新 更多