TypeScript 教程 第8章:枚举类型(enum)

📘 TypeScript 教程 第8章:枚举类型(enum)


一、枚举类型概述

在 TypeScript 中,枚举(enum) 是一种特殊的类,用于定义命名的常量集合。它可以帮助开发者将一组相关的数值或字符串组织在一起,提高代码可读性和维护性。

✅ 枚举的用途:

  • 定义有限的状态集合(如订单状态、用户角色)
  • 替代魔法数字或字符串
  • 提高类型安全性
  • 支持反向映射(从值到名称)

二、数字枚举(Numeric Enums)

✅ 基本语法:

enum Direction { //枚举方位
  Up,   //上
  Down, //下
  Left, //左
  Right //右
}

默认情况下,第一个成员的值为 0,后续依次递增:

console.log(Direction.Up);    // 输出: 0
console.log(Direction[0]);    // 输出: "Up"

🧠 自定义初始值:

enum Status {     //枚举状态码
  Success = 200,  //成功
  NotFound = 404, //页面丢失
  Error = 500     //失败
}

三、字符串枚举(String Enums)

字符串枚举每个成员都必须手动赋值为字符串。

✅ 示例:

enum UserRole {        //枚举用户角色
  Admin = 'ADMIN',     //admin管理员角色
  Editor = 'EDITOR',   //editor编辑角色
  Viewer = 'VIEWER'    //viewer游客角色
}

⚠️ 特点:

  • 不支持反向映射(因为无法从字符串推断出键名)
  • 更适合语义清晰的 API 接口、日志输出等场景

四、异构枚举(Heterogeneous Enums)

混合使用数字和字符串的枚举,虽然合法但不推荐。

示例:

// 定义一个枚举类型 ResponseType,用于表示不同的响应类型
enum ResponseType {  
  Text = 'TEXT',// 定义一个枚举成员 Text,并将其值显式设置为字符串 'TEXT'  
  Json = 1,     // 定义一个枚举成员 Json,并将其值显式设置为数字 1 
  Binary        // 定义一个枚举成员 Binary,没有显式赋值,因此它的值会自动递增
                // 由于前一个成员 Json 的值是 1,Binary 的值将是 2
}

// 使用示例
let responseType: ResponseType = ResponseType.Text; // 将 responseType 设置为 ResponseType.Text
console.log(responseType); // 输出: TEXT

responseType = ResponseType.Json; // 将 responseType 设置为 ResponseType.Json
console.log(responseType); // 输出: 1

responseType = ResponseType.Binary; // 将 responseType 设置为 ResponseType.Binary
console.log(responseType); // 输出: 2

说明

  • 枚举定义enum ResponseType 定义了一个新的枚举类型,用于表示不同的响应类型。
  • 枚举成员
    • Text = 'TEXT':将 Text 成员的值显式设置为字符串 'TEXT'
    • Json = 1:将 Json 成员的值显式设置为数字 1
    • Binary:没有显式赋值,因此它的值会自动递增。由于前一个成员 Json 的值是 1Binary 的值将是 2
  • 使用示例:展示了如何使用枚举类型,包括如何赋值和输出枚举成员的值。

五、常量枚举(const enum)

const enum 是一种编译时优化的枚举,不会生成实际的 JavaScript 对象。

✅ 示例:

// 定义一个常量枚举 LogLevel,用于表示不同的日志级别
const enum LogLevel {
  Info,    // 默认值为 0
  Warning, // 默认值为 1
  Error    // 默认值为 2
}

// 使用常量枚举
let level = LogLevel.Error; // level 的值将被替换为 2
console.log(level); // 输出: 2

编译后:

let level = 2 /* Error */;

⚠️ 注意事项:

  • 不能进行反向映射
  • 适用于性能敏感或打包体积要求高的项目

六、反向映射(Reverse Mapping)

对于数字枚举,TypeScript 会自动生成一个从值到名称的映射。

示例:

enum Direction {
  Up,
  Down,
  Left,
  Right
}

console.log(Direction[0]); // 输出: "Up"
console.log(Direction["Up"]); // 输出: 0

❗ 字符串枚举没有反向映射功能


七、枚举与类型守卫(Type Guards)

可以使用类型守卫来判断变量是否是某个枚举值。

示例:

// 定义一个普通枚举 Status,用于表示用户的状态
enum Status {
  Active,   // 默认值为 0
  Inactive  // 默认值为 1
}

// 定义一个函数 checkStatus,接受一个 Status 类型的参数 status
function checkStatus(status: Status): void {
  if (status === Status.Active) {
    console.log('User is active');
  } else {
    console.log('User is inactive');
  }
}

// 使用示例
checkStatus(Status.Active);   // 输出: User is active
checkStatus(Status.Inactive); // 输出: User is inactive

说明

  • 枚举定义

    • enum Status 定义了一个普通枚举,用于表示用户的状态。
    • ActiveInactive 是枚举成员,默认情况下,它们的值会自动递增,从 0 开始。
    • 因此,Active 的值是 0Inactive 的值是 1
  • 函数 checkStatus

    • 函数 checkStatus 接受一个参数 status,其类型为 Status
    • 函数内部通过比较 statusStatus.Active 来判断用户的状态,并输出相应的消息。
    • 如果 statusStatus.Active,则输出 'User is active'
    • 否则,输出 'User is inactive'
  • 使用示例

    • checkStatus(Status.Active); 调用函数并传入 Status.Active,输出 'User is active'
    • checkStatus(Status.Inactive); 调用函数并传入 Status.Inactive,输出 'User is inactive'

注意事项

  • 反向映射:普通枚举支持反向映射,这意味着你可以通过值来获取枚举成员的名称。例如,Status[0] 将返回 'Active'
  • 运行时对象:普通枚举在编译后会生成一个 JavaScript 对象,因此它可以在运行时使用,并支持反向映射。

通过这种方式,你可以在 TypeScript 中使用枚举来提高代码的可读性和类型安全性。


八、枚举在状态管理中的应用

✅ 场景示例:订单状态管理

// 定义一个枚举 OrderStatus,用于表示订单的不同状态
enum OrderStatus {
  Pending = 'pending',
  Processing = 'processing',
  Shipped = 'shipped',
  Delivered = 'delivered',
  Cancelled = 'cancelled'
}

// 定义一个类型别名 Order,表示一个订单对象
type Order = {
  id: number;
  status: OrderStatus;
};

// 定义一个函数 updateOrderStatus,用于更新订单的状态
function updateOrderStatus(order: Order, newStatus: OrderStatus): Order {
  return { ...order, status: newStatus };
}

// 使用示例
let order: Order = { id: 1, status: OrderStatus.Pending };
console.log(order); // 输出: { id: 1, status: 'pending' }

order = updateOrderStatus(order, OrderStatus.Processing);
console.log(order); // 输出: { id: 1, status: 'processing' }

order = updateOrderStatus(order, OrderStatus.Shipped);
console.log(order); // 输出: { id: 1, status: 'shipped' }

✅ 场景示例:用户角色权限控制

// 定义一个枚举 UserRole,用于表示用户的角色
enum UserRole {
  Admin = 'admin',
  Editor = 'editor',
  Guest = 'guest'
}

// 定义一个函数 canEditContent,用于检查用户角色是否可以编辑内容
function canEditContent(role: UserRole): boolean {
  return role === UserRole.Admin || role === UserRole.Editor;
}

// 使用示例
console.log(canEditContent(UserRole.Admin));   // 输出: true
console.log(canEditContent(UserRole.Editor));  // 输出: true
console.log(canEditContent(UserRole.Guest));   // 输出: false

九、枚举的高级用法

✅ 枚举作为联合类型的替代方案

type Status = 'success' | 'error' | 'loading';
// 等价于
enum StatusEnum {
  Success = 'success',
  Error = 'error',
  Loading = 'loading'
}

✅ 使用枚举简化 switch-case 逻辑

// 定义一个枚举 PaymentMethod,用于表示不同的支付方法
enum PaymentMethod {
  CreditCard,    // 默认值为 0
  PayPal,        // 默认值为 1
  BankTransfer   // 默认值为 2
}

// 定义一个函数 processPayment,用于处理支付
function processPayment(method: PaymentMethod): void {
  switch (method) {
    case PaymentMethod.CreditCard:
      console.log('Processing credit card...');
      break;
    case PaymentMethod.PayPal:
      console.log('Processing PayPal...');
      break;
    case PaymentMethod.BankTransfer:
      console.log('Processing bank transfer...');
      break;
    default:
      console.warn('Unknown payment method');
  }
}

// 使用示例
processPayment(PaymentMethod.CreditCard);    // 输出: Processing credit card...
processPayment(PaymentMethod.PayPal);        // 输出: Processing PayPal...
processPayment(PaymentMethod.BankTransfer);  // 输出: Processing bank transfer...

十、枚举 vs 联合类型 vs 类型别名

特性枚举(enum)联合类型(union)类型别名(type)
定义与用途定义一组命名的常量,用于表示一组有限的选项或状态。允许一个变量可以是多种类型之一,用于表示一组可能的值。为已有类型或复杂类型创建新的名称,简化代码书写或提高可读性。
内存占用编译后生成 JavaScript 对象,占用一定内存空间。不占用额外内存,只是类型检查层面的概念。不占用额外内存,只是类型的别名。
成员访问通过枚举常量名访问,枚举变量可赋值为枚举常量。不能直接“访问”联合类型的成员,但可以通过类型守卫(如 typeofinstanceof)来缩小类型范围。通过别名类型名访问,与原始类型用法相同。
类型安全提供类型检查,避免将无效值赋给枚举变量。提供类型检查,确保变量只能是联合类型中定义的其中一种类型。类型安全取决于原始类型或复杂类型的定义,别名本身不改变类型安全性。
使用场景适用于表示一组有限的、互斥的选项或状态,如状态机、配置选项等。适用于需要表示一个变量可以是多种类型之一的场景,如错误处理、API 响应类型等。适用于简化复杂类型的书写、提高代码可读性或封装平台相关类型。
反向映射数字枚举支持反向映射(从值到名称的映射),字符串枚举不支持。不支持反向映射。不支持反向映射。
编译结果编译为 JavaScript 对象,包含键值对。编译后无额外代码生成,只是类型信息。编译后无额外代码生成,只是类型信息。
与接口(interface)的结合可与接口结合使用,定义对象的结构和枚举类型。可与接口结合使用,定义对象的结构,其中某些属性可以是联合类型。常用于为接口或复杂类型创建别名,简化代码。
扩展性枚举是封闭的,一旦定义不能轻易扩展。联合类型是开放的,可以随时添加新的类型到联合中。类型别名本身不限制扩展性,取决于其定义的原始类型或复杂类型。
适用性适用于需要明确命名和有限选项的场景。适用于需要灵活性和多种可能性的场景。适用于需要简化类型书写和提高代码可读性的场景。

总结

  • 枚举:适用于需要明确命名和有限选项的场景,如状态机、配置选项等。它提供了类型安全和反向映射(数字枚举),但编译后会生成 JavaScript 对象,占用一定内存空间。
  • 联合类型:适用于需要表示一个变量可以是多种类型之一的场景,如错误处理、API 响应类型等。它提供了类型安全,但不占用额外内存,只是类型检查层面的概念。
  • 类型别名:适用于简化复杂类型的书写、提高代码可读性或封装平台相关类型。它本身不改变类型安全性,只是为已有类型或复杂类型创建新的名称。

十一、10道高频面试题(含详解)

Q1: 什么是 TypeScript 的枚举?有什么作用?

答案:
枚举是一种用于定义命名常量集合的类型,常用于表示固定状态集合、角色权限、配置项等,提高代码可读性和类型安全。


Q2: 数字枚举与字符串枚举的区别是什么?

答案:

  • 数字枚举默认从 0 开始自动递增,支持反向映射;
  • 字符串枚举必须手动赋值,更易读但不支持反向映射。

Q3: 如何访问枚举的名称和值?

答案:

enum Direction {
  Up,
  Down
}

console.log(Direction.Up);     // 输出: 0
console.log(Direction[0]);     // 输出: "Up"

Q4: const enum 和普通 enum 的区别?

答案:

  • const enum 在编译时会被内联替换,不生成实际 JS 对象;
  • 没有反向映射;
  • 更适合性能优化或减少打包体积。

Q5: 枚举可以被继承吗?

答案:
不可以。枚举是静态的,不支持继承、实现接口等面向对象特性。


Q6: 下面代码的输出是什么?

enum Status {
  Success = 200,
  NotFound = 404
}

console.log(Status['Success']); // ?
console.log(Status[200]);       // ?

答案:

  • Status['Success']200
  • Status[200]"Success"

Q7: 如何防止枚举被修改?

答案:
枚举本身是常量集合,不可变。如果需要动态修改,应使用对象或 Map 结构。


Q8: 枚举可以包含哪些类型的值?

答案:

  • 数字(默认)
  • 字符串
  • 混合类型(不推荐)

Q9: 如何判断一个变量是否属于某个枚举?

答案:

enum Role {
  Admin,
  Editor
}

function isValidRole(role: any): role is Role {
  return Object.values(Role).includes(role);
}

Q10: 枚举和联合类型哪个更好?为什么?

答案:

  • 枚举更适合状态管理、常量集合、带语义的选项;
  • 联合类型更灵活,适合组合多个类型或函数参数;
  • 枚举更直观,联合类型更轻量,根据场景选择。

十二、总结

项目内容
核心概念枚举(enum)、数字枚举、字符串枚举、常量枚举、反向映射
应用场景状态管理、角色权限、配置项、API 返回码
优点可读性强、类型安全、便于维护
缺点不支持继承、字符串枚举无反向映射
最佳实践使用数字枚举处理状态;字符串枚举用于 API 接口;避免异构枚举
常见考点枚举与联合类型对比、反向映射机制、const enum 编译优化、枚举作为类型守卫

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

全栈前端老曹

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值