枚举类型

枚举用于定义一组命名的常量,使代码更具可读性和可维护性。

数字枚举

默认情况下,枚举值从 0 开始递增:

enum Direction {
  Up,
  Down,
  Left,
  Right
}

console.log(Direction.Up)     // 0
console.log(Direction.Down)   // 1
console.log(Direction.Left)   // 2
console.log(Direction.Right)  // 3

自定义起始值

enum Direction {
  Up = 1,
  Down,
  Left,
  Right
}

console.log(Direction.Up)     // 1
console.log(Direction.Down)   // 2

全部自定义

enum Direction {
  Up = 1,
  Down = 3,
  Left = 5,
  Right = 7
}

字符串枚举

enum Direction {
  Up = "UP",
  Down = "DOWN",
  Left = "LEFT",
  Right = "RIGHT"
}

console.log(Direction.Up)  // "UP"

反向映射

数字枚举支持反向映射:

enum Direction {
  Up,
  Down
}

console.log(Direction[0])  // "Up"
console.log(Direction["Up"])  // 0

字符串枚举不支持反向映射。

常量枚举

使用 const 定义的枚举会在编译时内联:

const enum Direction {
  Up,
  Down,
  Left,
  Right
}

let dir = Direction.Up

// 编译后
// let dir = 0

常量枚举性能更好,但不能反向映射。

异构枚举

混合数字和字符串(不推荐):

enum BooleanLikeHeterogeneousEnum {
  No = 0,
  Yes = "YES"
}

计算成员

enum FileAccess {
  None,
  Read = 1 << 1,
  Write = 1 << 2,
  ReadWrite = Read | Write
}

console.log(FileAccess.Read)       // 2
console.log(FileAccess.Write)      // 4
console.log(FileAccess.ReadWrite)  // 6

实际应用

状态定义

enum OrderStatus {
  Pending = "PENDING",
  Paid = "PAID",
  Shipped = "SHIPPED",
  Delivered = "DELIVERED",
  Cancelled = "CANCELLED"
}

interface Order {
  id: number
  status: OrderStatus
}

function canCancel(order: Order): boolean {
  return order.status === OrderStatus.Pending
}

const order: Order = {
  id: 1,
  status: OrderStatus.Pending
}

console.log(canCancel(order))  // true

用户角色

enum Role {
  Guest = 0,
  User = 1,
  Admin = 2,
  SuperAdmin = 3
}

function hasPermission(role: Role, required: Role): boolean {
  return role >= required
}

console.log(hasPermission(Role.Admin, Role.User))  // true
console.log(hasPermission(Role.User, Role.Admin))  // false

颜色定义

enum Color {
  Red = "#FF0000",
  Green = "#00FF00",
  Blue = "#0000FF",
  Yellow = "#FFFF00"
}

function getContrastColor(color: Color): Color {
  switch (color) {
    case Color.Red:
      return Color.Blue
    case Color.Blue:
      return Color.Yellow
    default:
      return Color.Red
  }
}

星期几

enum Weekday {
  Monday = 1,
  Tuesday,
  Wednesday,
  Thursday,
  Friday,
  Saturday,
  Sunday
}

function isWeekend(day: Weekday): boolean {
  return day === Weekday.Saturday || day === Weekday.Sunday
}

console.log(isWeekend(Weekday.Saturday))  // true

枚举方法

获取所有枚举值

enum Color {
  Red,
  Green,
  Blue
}

function getEnumValues<T>(enumObj: T): T[keyof T][] {
  return Object.keys(enumObj)
    .filter(key => typeof enumObj[key as keyof T] === 'number')
    .map(key => enumObj[key as keyof T])
}

console.log(getEnumValues(Color))  // [0, 1, 2]

枚举键值对

enum Status {
  Active = "ACTIVE",
  Inactive = "INACTIVE"
}

const statusLabels: Record<Status, string> = {
  [Status.Active]: "激活",
  [Status.Inactive]: "未激活"
}

console.log(statusLabels[Status.Active])  // "激活"

注意事项

枚举 vs 联合类型

// 枚举
enum Direction {
  Up,
  Down
}

// 联合类型
type DirectionType = "Up" | "Down"

联合类型更轻量,枚举功能更丰富。

避免滥用

// 不推荐:简单场景用字面量
enum YesNo {
  Yes = "YES",
  No = "NO"
}

// 推荐:直接使用字面量
type YesNo = "yes" | "no"

编译产物

enum Direction {
  Up,
  Down
}

// 编译后生成
var Direction
(function (Direction) {
  Direction[Direction["Up"] = 0] = "Up"
  Direction[Direction["Down"] = 1] = "Down"
})(Direction || (Direction = {}))

常量枚举不会生成额外代码。