从TypeScript引入联合类型以来,我想知道是否有任何理由声明枚举类型。考虑以下枚举类型的声明:
enum X { A, B, C }
var x: X = X.A;
还有一个类似的联合类型声明:
type X: "A" | "B" | "C"
var x: X = "A";
如果它们基本上具有相同的作用,而联合更加强大且表达力更强,那么为什么需要枚举?从TypeScript引入联合类型以来,我想知道是否有任何理由声明枚举类型。考虑以下枚举类型的声明:
enum X { A, B, C }
var x: X = X.A;
还有一个类似的联合类型声明:
type X: "A" | "B" | "C"
var x: X = "A";
如果它们基本上具有相同的作用,而联合更加强大且表达力更强,那么为什么需要枚举?最近的TypeScript版本中,声明可迭代联合类型变得容易。因此,您应该优先使用联合类型而不是枚举。
const permissions = ['read', 'write', 'execute'] as const;
type Permission = typeof permissions[number]; // 'read' | 'write' | 'execute'
// you can iterate over permissions
for (const permission of permissions) {
// do something
}
当联合类型的实际值不能很好地描述它们自己时,您可以像枚举一样对它们进行命名。
// when you use enum
enum Permission {
Read = 'r',
Write = 'w',
Execute = 'x'
}
// union type equivalent
const Permission = {
Read: 'r',
Write: 'w',
Execute: 'x'
} as const;
type Permission = typeof Permission[keyof typeof Permission]; // 'r' | 'w' | 'x'
// of course it's quite easy to iterate over
for (const permission of Object.values(Permission)) {
// do something
}
我认为这个概念是TypeScript在其他altJS语言中变得如此流行的关键原因之一。 非常量枚举通过发出在运行时存活的JavaScript对象来违反该概念,其语法与JavaScript不兼容。
目前有两种解决方法:手动或使用插件babel-plugin-const-enum
来摆脱常量枚举。
当提供了--isolatedModules
标志时,不允许声明环境常量枚举。
TypeScript团队成员表示,在DT上使用"const enum
真的没有意义"(DT指DefinitelyTyped),并且应该在环境上下文中使用字面量(字符串或数字)的联合类型,而不是常量枚举。
--isolatedModules
标志下的常量枚举即使不在环境上下文中也会表现奇怪我很惊讶地看到GitHub上这条评论,并确认该行为仍然适用于TypeScript 3.8.2。
您可以将任何数字分配给数字枚举。
enum ZeroOrOne {
Zero = 0,
One = 1
}
const zeroOrOne: ZeroOrOne = 2; // no error!!
我们有时会看到这种类型的字符串枚举:
enum Day {
Sunday = 'Sunday',
Monday = 'Monday',
Tuesday = 'Tuesday',
Wednesday = 'Wednesday',
Thursday = 'Thursday',
Friday = 'Friday',
Saturday = 'Saturday'
}
即使从上下文中很明显字符串值包含在枚举中,也不能将其赋值给枚举。
enum StringEnum {
Foo = 'foo'
}
const foo1: StringEnum = StringEnum.Foo; // no error
const foo2: StringEnum = 'foo'; // error!!
通过消除使用字符串值或字符串字面量,这统一了整个代码中枚举值分配的样式。这种行为与 TypeScript 类型系统在其他地方的行为不一致,有些人认为应该修复这种情况,并提出了问题(this和 this),其中反复提到字符串枚举的意图是提供“不透明”的字符串类型:即它们可以在不修改使用者的情况下更改。
enum Weekend {
Saturday = 'Saturday',
Sunday = 'Sunday'
}
// As this style is forced, you can change the value of
// Weekend.Saturday to 'Sat' without modifying consumers
const weekend: Weekend = Weekend.Saturday;
enum Weekend {
Saturday = 'Saturday',
Sunday = 'Sunday'
}
// The change of the value of Weekend.Saturday to 'Sat'
// results in a compilation error
const saturday: 'Saturday' = Weekend.Saturday;
no-restricted-syntax
规则,如所述。declare const permissions: readonly ['read', 'write', 'execute']
和真实的 JavaScript 对象 const permissions = ['read', 'write', 'execute']
应该可以解决问题。 - kimamulaconst foo: StringEnum === StringEnum.Foo // true or false
我希望这个限制可以确保我们不会混淆字符串字面值。 - Matt有几个原因你可能想要使用枚举
:
枚举
。枚举
用作标志
。位标志我认为使用联合类型的重要优势在于它们提供了一种简洁的方式来表示具有多种类型的值,并且它们非常易读。 let x: number | string
编辑:自TypeScript 2.4起,枚举现在支持字符串。
enum Colors {
Red = "RED",
Green = "GREEN",
Blue = "BLUE",
}
enum
是一种反模式的人? - Cameron Tacklind枚举可以在概念上被视为联合类型的子集,专门用于 int
和/或 string
值,并具有其他一些在其他回答中提到的附加功能,使其易于使用,例如命名空间。
关于类型安全性,数字枚举最不安全,然后是联合类型,最后是字符串枚举:
// Numeric enum
enum Colors { Red, Green, Blue }
const c: Colors = 100; // ⚠️ No errors!
// Equivalent union types
type Color =
| 0 | 'Red'
| 1 | 'Green'
| 2 | 'Blue';
let color: Color = 'Red'; // ✔️ No error because namespace free
color = 100; // ✔️ Error: Type '100' is not assignable to type 'Color'
type AltColor = 'Red' | 'Yellow' | 'Blue';
let altColor: AltColor = 'Red';
color = altColor; // ⚠️ No error because `altColor` type is here narrowed to `"Red"`
// String enum
enum NamedColors {
Red = 'Red',
Green = 'Green',
Blue = 'Blue',
}
let namedColor: NamedColors = 'Red'; // ✔️ Error: Type '"Red"' is not assignable to type 'Colors'.
enum AltNamedColors {
Red = 'Red',
Yellow = 'Yellow',
Blue = 'Blue',
}
namedColor = AltNamedColors.Red; // ✔️ Error: Type 'AltNamedColors.Red' is not assignable to type 'Colors'.
在这篇2ality文章中可以了解更多关于这个主题的内容:TypeScript枚举:它们是如何工作的?可以用来做什么?
联合类型支持不同类型和结构的数据,使得多态成为可能:
class RGB {
constructor(
readonly r: number,
readonly g: number,
readonly b: number) { }
toHSL() {
return new HSL(0, 0, 0); // Fake formula
}
}
class HSL {
constructor(
readonly h: number,
readonly s: number,
readonly l: number) { }
lighten() {
return new HSL(this.h, this.s, this.l + 10);
}
}
function lightenColor(c: RGB | HSL) {
return (c instanceof RGB ? c.toHSL() : c).lighten();
}
在枚举类型和联合类型之间,单例类型可以替代枚举类型。这样更冗长但也更加面向对象:
class Color {
static readonly Red = new Color(1, 'Red', '#FF0000');
static readonly Green = new Color(2, 'Green', '#00FF00');
static readonly Blue = new Color(3, 'Blue', '#0000FF');
static readonly All: readonly Color[] = [
Color.Red,
Color.Green,
Color.Blue,
];
private constructor(
readonly id: number,
readonly label: string,
readonly hex: string) { }
}
const c = Color.Red;
const colorIds = Color.All.map(x => x.id);
我倾向于查看F#以了解良好的建模实践。以下是来自F# for fun and profit关于F#枚举的文章中的一句话,这里可能很有用:
通常情况下,你应该首选判别联合类型而不是枚举,除非你确实需要将它们与
int
(或string
)值相关联
还有其他的替代方案来对枚举进行建模。其中一些在这篇Typescript中替代枚举的文章中有很好地描述。
枚举类型并非多余,但在大多数情况下,联合类型更受青睐。
但并非总是如此。使用枚举类型来表示状态转换等内容比使用联合类型更方便和表达力更强。
考虑实际场景:
enum OperationStatus {
NEW = 1,
PROCESSING = 2,
COMPLETED = 4
}
OperationStatus.PROCESSING > OperationStatus.NEW // true
OperationStatus.PROCESSING > OperationStatus.COMPLETED // false