TypeScript枚举:让你的代码更有"选择权"

前言

大家好,我是小杨。在平时的开发中,我经常遇到需要定义一组相关常量的情况。比如订单状态、用户角色、颜色主题等等。最开始我都是用普通的常量来定义,直到我发现了TypeScript的枚举(Enum)特性,它让我的代码变得更加清晰和类型安全。今天就来和大家聊聊这个既实用又有趣的特性。

什么是枚举?从选择题到代码

想象一下你在做选择题:A、B、C、D四个选项,每个选项都有明确的含义。枚举就是代码世界中的"选择题",它让我们可以定义一组命名的常量,让代码更易读、更安全。

第一个枚举例子

让我从一个最简单的例子开始:

typescript 复制代码
// 定义一个表示星期的枚举
enum DayOfWeek {
  Sunday,
  Monday,
  Tuesday,
  Wednesday,
  Thursday,
  Friday,
  Saturday
}

// 使用枚举
const today: DayOfWeek = DayOfWeek.Wednesday;
const isWeekend = (day: DayOfWeek): boolean => {
  return day === DayOfWeek.Saturday || day === DayOfWeek.Sunday;
};

console.log(isWeekend(today)); // 输出: false
console.log(isWeekend(DayOfWeek.Sunday)); // 输出: true

看到这里你可能会有疑问:这些枚举值对应的是什么?让我打印出来看看:

typescript 复制代码
console.log(DayOfWeek.Sunday);    // 输出: 0
console.log(DayOfWeek.Monday);    // 输出: 1
console.log(DayOfWeek.Tuesday);   // 输出: 2

默认情况下,枚举成员从0开始自动编号。但我们可以自定义这些值。

枚举的多种用法

1. 数字枚举

typescript 复制代码
// 自定义数字值
enum HttpStatus {
  OK = 200,
  Created = 201,
  BadRequest = 400,
  Unauthorized = 401,
  NotFound = 404,
  InternalServerError = 500
}

function handleResponse(status: HttpStatus) {
  switch (status) {
    case HttpStatus.OK:
      console.log("请求成功");
      break;
    case HttpStatus.NotFound:
      console.log("资源未找到");
      break;
    case HttpStatus.InternalServerError:
      console.log("服务器错误");
      break;
    default:
      console.log("其他状态码:", status);
  }
}

// 使用
handleResponse(HttpStatus.OK); // 输出: 请求成功

2. 字符串枚举

在实际项目中,我更喜欢使用字符串枚举,因为它们更容易调试:

typescript 复制代码
enum UserRole {
  Admin = "ADMIN",
  Editor = "EDITOR", 
  Viewer = "VIEWER",
  Guest = "GUEST"
}

enum ApiEndpoints {
  Login = "/api/auth/login",
  Logout = "/api/auth/logout",
  Users = "/api/users",
  Products = "/api/products"
}

function checkPermission(role: UserRole): boolean {
  return role === UserRole.Admin || role === UserRole.Editor;
}

// 使用
const currentUserRole = UserRole.Admin;
console.log(checkPermission(currentUserRole)); // 输出: true
console.log(ApiEndpoints.Login); // 输出: "/api/auth/login"

3. 常量枚举

如果你关心性能,常量枚举是个不错的选择:

typescript 复制代码
const enum Direction {
  Up = "UP",
  Down = "DOWN", 
  Left = "LEFT",
  Right = "RIGHT"
}

// 使用常量枚举
const move = (dir: Direction) => {
  console.log(`Moving ${dir}`);
};

move(Direction.Up); // 编译后:move("UP")

常量枚举在编译时会被完全删除,只保留具体的值,可以减少代码体积。

4. 异构枚举

虽然不常用,但TypeScript也支持数字和字符串混合的枚举:

typescript 复制代码
enum MixedEnum {
  No = 0,
  Yes = "YES",
  Maybe = 2
}

实战场景:枚举在项目中的应用

场景1:状态管理

在我的电商项目中,枚举在订单状态管理中发挥了重要作用:

typescript 复制代码
enum OrderStatus {
  Pending = "PENDING",
  Confirmed = "CONFIRMED", 
  Processing = "PROCESSING",
  Shipped = "SHIPPED",
  Delivered = "DELIVERED",
  Cancelled = "CANCELLED",
  Refunded = "REFUNDED"
}

class Order {
  constructor(
    public id: number,
    public status: OrderStatus,
    public createdAt: Date
  ) {}
  
  canBeCancelled(): boolean {
    const cancellableStatuses = [
      OrderStatus.Pending,
      OrderStatus.Confirmed,
      OrderStatus.Processing
    ];
    return cancellableStatuses.includes(this.status);
  }
  
  getStatusText(): string {
    const statusTexts = {
      [OrderStatus.Pending]: "待处理",
      [OrderStatus.Confirmed]: "已确认",
      [OrderStatus.Processing]: "处理中",
      [OrderStatus.Shipped]: "已发货", 
      [OrderStatus.Delivered]: "已送达",
      [OrderStatus.Cancelled]: "已取消",
      [OrderStatus.Refunded]: "已退款"
    };
    return statusTexts[this.status];
  }
}

// 使用示例
const order = new Order(1, OrderStatus.Pending, new Date());
console.log(order.canBeCancelled()); // 输出: true
console.log(order.getStatusText());  // 输出: 待处理

场景2:配置系统

在应用配置中,枚举让配置更加类型安全:

typescript 复制代码
enum Environment {
  Development = "development",
  Staging = "staging",
  Production = "production"
}

enum LogLevel {
  Error = "error",
  Warn = "warn", 
  Info = "info",
  Debug = "debug"
}

enum Theme {
  Light = "light",
  Dark = "dark",
  Auto = "auto"
}

class AppConfig {
  constructor(
    public env: Environment,
    public logLevel: LogLevel,
    public theme: Theme,
    public features: {
      analytics: boolean;
      notifications: boolean;
    }
  ) {}
  
  isProduction(): boolean {
    return this.env === Environment.Production;
  }
  
  shouldLogDebug(): boolean {
    return this.logLevel === LogLevel.Debug;
  }
}

// 使用示例
const config = new AppConfig(
  Environment.Development,
  LogLevel.Debug,
  Theme.Auto,
  { analytics: true, notifications: false }
);

console.log(config.isProduction()); // 输出: false

场景3:权限系统

在用户权限管理中,枚举让权限检查更加清晰:

typescript 复制代码
enum Permission {
  ReadUsers = "users:read",
  WriteUsers = "users:write", 
  DeleteUsers = "users:delete",
  ReadProducts = "products:read",
  WriteProducts = "products:write",
  DeleteProducts = "products:delete",
  ManageOrders = "orders:manage"
}

enum UserRole {
  SuperAdmin = "SUPER_ADMIN",
  Admin = "ADMIN",
  Manager = "MANAGER", 
  Customer = "CUSTOMER"
}

class PermissionManager {
  private rolePermissions: Record<UserRole, Permission[]> = {
    [UserRole.SuperAdmin]: Object.values(Permission),
    [UserRole.Admin]: [
      Permission.ReadUsers,
      Permission.WriteUsers,
      Permission.ReadProducts, 
      Permission.WriteProducts,
      Permission.ManageOrders
    ],
    [UserRole.Manager]: [
      Permission.ReadProducts,
      Permission.WriteProducts,
      Permission.ManageOrders
    ],
    [UserRole.Customer]: [
      Permission.ReadProducts
    ]
  };
  
  hasPermission(role: UserRole, permission: Permission): boolean {
    return this.rolePermissions[role]?.includes(permission) || false;
  }
  
  getPermissionsForRole(role: UserRole): Permission[] {
    return this.rolePermissions[role] || [];
  }
}

// 使用示例
const permissionManager = new PermissionManager();
const canDeleteUsers = permissionManager.hasPermission(
  UserRole.Admin, 
  Permission.DeleteUsers
);
console.log(canDeleteUsers); // 输出: false

场景4:国际化

在多语言项目中,枚举可以帮助管理语言选项:

typescript 复制代码
enum Language {
  English = "en",
  Chinese = "zh",
  Japanese = "ja",
  Korean = "ko",
  French = "fr"
}

enum Currency {
  USD = "USD",
  CNY = "CNY", 
  JPY = "JPY",
  EUR = "EUR",
  KRW = "KRW"
}

class LocalizationService {
  private translations: Record<Language, Record<string, string>> = {
    [Language.English]: {
      welcome: "Welcome",
      goodbye: "Goodbye",
      error: "An error occurred"
    },
    [Language.Chinese]: {
      welcome: "欢迎",
      goodbye: "再见", 
      error: "发生错误"
    }
    // 其他语言...
  };
  
  constructor(private language: Language) {}
  
  setLanguage(language: Language): void {
    this.language = language;
  }
  
  translate(key: string): string {
    return this.translations[this.language]?.[key] || key;
  }
  
  formatCurrency(amount: number, currency: Currency): string {
    const formatters: Record<Currency, string> = {
      [Currency.USD]: `$${amount.toFixed(2)}`,
      [Currency.CNY]: `¥${amount.toFixed(2)}`,
      [Currency.JPY]: `¥${amount}`,
      [Currency.EUR]: `€${amount.toFixed(2)}`,
      [Currency.KRW]: `₩${amount.toLocaleString()}`
    };
    return formatters[currency];
  }
}

// 使用示例
const i18n = new LocalizationService(Language.Chinese);
console.log(i18n.translate("welcome")); // 输出: 欢迎
console.log(i18n.formatCurrency(100, Currency.CNY)); // 输出: ¥100.00

枚举的最佳实践

1. 使用字符串枚举提高可读性

typescript 复制代码
// 推荐:字符串枚举
enum UserAction {
  Login = "USER_LOGIN",
  Logout = "USER_LOGOUT", 
  UpdateProfile = "USER_UPDATE_PROFILE"
}

// 不推荐:数字枚举(调试时难以理解)
enum UserActionOld {
  Login,    // 0
  Logout,   // 1
  UpdateProfile // 2
}

2. 使用常量枚举优化性能

typescript 复制代码
// 在性能敏感的场景使用常量枚举
const enum ResponseCode {
  Success = 200,
  NotFound = 404,
  ServerError = 500
}

// 编译后,这些枚举引用会被替换为具体数值
const code = ResponseCode.Success; // 编译为: const code = 200

3. 避免异构枚举

typescript 复制代码
// 不推荐:混合数字和字符串
enum BadExample {
  A = 1,
  B = "B",
  C = 2
}

// 推荐:保持一致性
enum GoodExample {
  A = "A",
  B = "B", 
  C = "C"
}

enum GoodExample2 {
  A = 1,
  B = 2,
  C = 3
}

4. 使用命名空间扩展枚举

typescript 复制代码
enum NotificationType {
  Email = "EMAIL",
  SMS = "SMS",
  Push = "PUSH"
}

namespace NotificationType {
  export function isUrgent(type: NotificationType): boolean {
    return type === NotificationType.SMS || type === NotificationType.Push;
  }
  
  export function getChannels(): NotificationType[] {
    return Object.values(NotificationType);
  }
}

// 使用示例
console.log(NotificationType.isUrgent(NotificationType.Email)); // false
console.log(NotificationType.getChannels()); // ["EMAIL", "SMS", "PUSH"]

枚举的替代方案

虽然枚举很强大,但在某些情况下,使用联合类型可能更合适:

typescript 复制代码
// 使用联合类型
type OrderStatus = "PENDING" | "CONFIRMED" | "SHIPPED" | "DELIVERED";

// 使用对象常量
const ORDER_STATUS = {
  PENDING: "PENDING",
  CONFIRMED: "CONFIRMED", 
  SHIPPED: "SHIPPED",
  DELIVERED: "DELIVERED"
} as const;

type OrderStatusType = typeof ORDER_STATUS[keyof typeof ORDER_STATUS];

选择建议:

  • 需要迭代枚举成员时:使用枚举
  • 只需要类型安全时:使用联合类型
  • 需要同时使用值和类型时:使用对象常量 + 类型

结语

枚举是TypeScript中一个非常实用的特性,它让我们的代码更加清晰、安全。通过合理使用枚举,我们可以更好地表达业务逻辑中的各种状态和选项。

记住,工具虽好,但也要用在合适的地方。希望今天的分享能帮助你在实际项目中更好地使用TypeScript枚举!

⭐ 写在最后

请大家不吝赐教,在下方评论或者私信我,十分感谢🙏🙏🙏.

✅ 认为我某个部分的设计过于繁琐,有更加简单或者更高逼格的封装方式

✅ 认为我部分代码过于老旧,可以提供新的API或最新语法

✅ 对于文章中部分内容不理解

✅ 解答我文章中一些疑问

✅ 认为某些交互,功能需要优化,发现BUG

✅ 想要添加新功能,对于整体的设计,外观有更好的建议

✅ 一起探讨技术加qq交流群:906392632

最后感谢各位的耐心观看,既然都到这了,点个 👍赞再走吧!

相关推荐
kingwebo'sZone5 分钟前
C#使用Aspose.Words把 word转成图片
前端·c#·word
xjt_090124 分钟前
基于 Vue 3 构建企业级 Web Components 组件库
前端·javascript·vue.js
我是伪码农36 分钟前
Vue 2.3
前端·javascript·vue.js
夜郎king1 小时前
HTML5 SVG 实现日出日落动画与实时天气可视化
前端·html5·svg 日出日落
辰风沐阳1 小时前
JavaScript 的宏任务和微任务
javascript
夏幻灵2 小时前
HTML5里最常用的十大标签
前端·html·html5
冰暮流星2 小时前
javascript之二重循环练习
开发语言·javascript·数据库
Mr Xu_2 小时前
Vue 3 中 watch 的使用详解:监听响应式数据变化的利器
前端·javascript·vue.js
未来龙皇小蓝2 小时前
RBAC前端架构-01:项目初始化
前端·架构
程序员agions2 小时前
2026年,微前端终于“死“了
前端·状态模式