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

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

相关推荐
江城开朗的豌豆3 小时前
TypeScript接口:打造你的代码“契约”之道
前端·javascript
江城开朗的豌豆3 小时前
TypeScript类:面向对象编程的超级武器
前端·javascript
鹏多多3 小时前
React项目使用useMemo优化性能指南和应用场景
前端·javascript·react.js
dllxhcjla3 小时前
css第一天
java·前端·css
charlie1145141913 小时前
CSS学习笔记6:定位与布局
前端·css·笔记·学习·css3·教程
自由日记3 小时前
css学习盒模型:
前端·css·学习
午安~婉3 小时前
浏览器与网络
前端·javascript·网络·http·浏览器
岁月宁静3 小时前
大规模图片列表性能优化:基于 IntersectionObserver 的懒加载与滚动加载方案
前端·javascript·vue.js
爆爆凯3 小时前
Spring Boot Web上下文工具类详解:获取Request、Response和参数
前端·spring boot·后端