如何在 TypeScript 中合并两个枚举
Posted
技术标签:
【中文标题】如何在 TypeScript 中合并两个枚举【英文标题】:How to merge two enums in 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 中实现这样的功能?
【问题讨论】:
【参考方案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”一样,编译器知道这些类型。
因此,结合 Foods
和 Food
您将获得标准枚举行为。
【讨论】:
【参考方案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】:
枚举、接口和类型 - 合并枚举的有效解决方案
这里令人困惑的是类型与值。
如果您定义一个值(let
、const
等),它将有一个值加上一些计算但未单独命名的类型。
如果你定义了一个type
或interface
,它会创建一个命名类型,但不会以任何方式在最终的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
【讨论】:
合并似乎有效,但我不能在界面中使用它。下面的游乐场演示。 @陶 TS Playground Demo 我想问题是合并的对象不再是 TypeScript 的枚举,尽管它的行为相同。作为一种解决方法,您可以编写typeof AnimalTypes.Bats
,它将解析为Mammals.Bats
。也许其他人知道更好的方法。以上是关于如何在 TypeScript 中合并两个枚举的主要内容,如果未能解决你的问题,请参考以下文章