📘 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
的值是1
,Binary
的值将是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
定义了一个普通枚举,用于表示用户的状态。Active
和Inactive
是枚举成员,默认情况下,它们的值会自动递增,从0
开始。- 因此,
Active
的值是0
,Inactive
的值是1
。
-
函数
checkStatus
:- 函数
checkStatus
接受一个参数status
,其类型为Status
。 - 函数内部通过比较
status
和Status.Active
来判断用户的状态,并输出相应的消息。 - 如果
status
是Status.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 对象,占用一定内存空间。 | 不占用额外内存,只是类型检查层面的概念。 | 不占用额外内存,只是类型的别名。 |
成员访问 | 通过枚举常量名访问,枚举变量可赋值为枚举常量。 | 不能直接“访问”联合类型的成员,但可以通过类型守卫(如 typeof 、instanceof )来缩小类型范围。 | 通过别名类型名访问,与原始类型用法相同。 |
类型安全 | 提供类型检查,避免将无效值赋给枚举变量。 | 提供类型检查,确保变量只能是联合类型中定义的其中一种类型。 | 类型安全取决于原始类型或复杂类型的定义,别名本身不改变类型安全性。 |
使用场景 | 适用于表示一组有限的、互斥的选项或状态,如状态机、配置选项等。 | 适用于需要表示一个变量可以是多种类型之一的场景,如错误处理、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 编译优化、枚举作为类型守卫 |