前言
大家好,我是小杨。在平时的开发中,我经常遇到需要定义一组相关常量的情况。比如订单状态、用户角色、颜色主题等等。最开始我都是用普通的常量来定义,直到我发现了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
最后感谢各位的耐心观看,既然都到这了,点个 👍赞再走吧!