1. 基础枚举类型
数值枚举(默认)
enum Direction {
Up, // 0
Down, // 1
Left, // 2
Right // 3
}
// 自定义起始值
enum Status {
Pending = 1,
Approved, // 2
Rejected // 3
}
字符串枚举
enum Message {
Success = "SUCCESS",
Error = "ERROR",
Warning = "WARNING"
}
// 运行时保留为字符串,提供更好的可读性
console.log(Message.Success); // "SUCCESS"
异构枚举(混合)
enum MixedEnum {
No = 0,
Yes = "YES",
Maybe = 2 // 需要手动指定
}
2. 常量枚举(性能优化)
// 编译时会被完全移除,减少运行时开销
const enum Colors {
Red = "#FF0000",
Green = "#00FF00",
Blue = "#0000FF"
}
// 编译后:const color = "#FF0000";
const color = Colors.Red;
// ⚠️ 注意:常量枚举不能有计算成员
const enum Size {
Small = getSize(), // 错误:计算成员
Medium = 2
}
3. 计算成员和常量成员
enum FileAccess {
// 常量成员
None,
Read = 1 << 1, // 2
Write = 1 << 2, // 4
ReadWrite = Read | Write, // 6
// 计算成员(运行时计算)
Computed = "file".length, // 4
// 计算成员后的成员必须初始化
Another = Computed + 1 // 5
}
4. 反向映射(数值枚举特有)
enum Weekday {
Monday = 1,
Tuesday,
Wednesday
}
// 正向访问
console.log(Weekday.Monday); // 1
// 反向访问(只适用于数值枚举)
console.log(Weekday[1]); // "Monday"
console.log(Weekday[Weekday.Monday]); // "Monday"
// 字符串枚举没有反向映射
enum StringEnum {
A = "a"
}
console.log(StringEnum["a"]); // undefined ❌
5. 联合枚举类型
enum ShapeKind {
Circle,
Square,
}
interface Circle {
kind: ShapeKind.Circle; // 类型为 ShapeKind.Circle (0)
radius: number;
}
interface Square {
kind: ShapeKind.Square; // 类型为 ShapeKind.Square (1)
sideLength: number;
}
// 类型保护:TypeScript 知道 kind 只能是特定值
function getArea(shape: Circle | Square) {
switch (shape.kind) {
case ShapeKind.Circle:
// 这里 shape 被推断为 Circle
return Math.PI * shape.radius ** 2;
case ShapeKind.Square:
// 这里 shape 被推断为 Square
return shape.sideLength ** 2;
}
}
6. 枚举作为对象使用
// 通过 namespace 扩展枚举
enum LogLevel {
Error,
Warning,
Info,
Debug
}
namespace LogLevel {
// 添加辅助方法
export function getColor(level: LogLevel): string {
switch (level) {
case LogLevel.Error: return "red";
case LogLevel.Warning: return "yellow";
case LogLevel.Info: return "blue";
case LogLevel.Debug: return "gray";
}
}
// 添加属性
export const prefix = "[LOG]";
}
console.log(LogLevel.getColor(LogLevel.Error)); // "red"
7. 运行时技巧
遍历枚举
enum Fruit {
Apple,
Banana,
Orange
}
// 获取所有键
const keys = Object.keys(Fruit).filter(k => isNaN(Number(k)));
// ["Apple", "Banana", "Orange"]
// 获取所有值
const values = Object.values(Fruit).filter(v => typeof v === "number");
// [0, 1, 2]
// 安全遍历
function getEnumValues<T extends object>(enumObj: T): Array<T[keyof T]> {
return Object.values(enumObj).filter(
value => typeof value === "number" || typeof value === "string"
) as Array<T[keyof T]>;
}
枚举检查
enum UserRole {
Admin = "admin",
User = "user",
Guest = "guest"
}
// 检查值是否为有效枚举值
function isValidRole(role: string): role is UserRole {
return Object.values(UserRole).includes(role as UserRole);
}
// 类型安全的获取
function getRole(key: string): UserRole | undefined {
return Object.values(UserRole).find(value => value === key);
}
8. 高级模式
标志位枚举(Flags)
enum Permissions {
None = 0,
Read = 1 << 0, // 1
Write = 1 << 1, // 2
Delete = 1 << 2, // 4
Execute = 1 << 3, // 8
All = Read | Write | Delete | Execute // 15
}
// 使用
const userPermissions = Permissions.Read | Permissions.Write; // 3
// 检查权限
function hasPermission(userPerms: Permissions, required: Permissions): boolean {
return (userPerms & required) === required;
}
// 添加权限
function addPermission(userPerms: Permissions, perm: Permissions): Permissions {
return userPerms | perm;
}
枚举映射
// 创建类型安全的映射
enum EventType {
Click = "click",
Hover = "hover",
Focus = "focus"
}
type EventHandlers = {
[K in EventType]: () => void;
};
const handlers: EventHandlers = {
[EventType.Click]: () => console.log("clicked"),
[EventType.Hover]: () => console.log("hovered"),
[EventType.Focus]: () => console.log("focused")
};
9. 最佳实践
使用 const enum 优化性能
// 生产代码中使用 const enum
const enum ApiStatus {
Loading,
Success,
Error
}
// 开发调试时使用普通 enum
// enum ApiStatus { ... }
避免魔法数字/字符串
// ❌ 不好
if (status === 1) { /* ... */ }
// ✅ 好
enum TaskStatus {
Pending = 1,
InProgress = 2,
Completed = 3
}
if (status === TaskStatus.InProgress) { /* ... */ }
使用字符串枚举提高可读性
// ❌ 数值枚举在日志中难以理解
console.log(ErrorType[1]); // 需要查文档
// ✅ 字符串枚举直接可读
enum ErrorType {
NotFound = "NOT_FOUND",
Unauthorized = "UNAUTHORIZED"
}
console.log(ErrorType.NotFound); // "NOT_FOUND"
10. 常见问题解决
枚举值类型安全
// 防止无效值
enum Color {
Red = "red",
Green = "green",
Blue = "blue"
}
// 使用类型守卫
function isColor(value: string): value is Color {
return Object.values(Color).includes(value as Color);
}
// 运行时验证
function parseColor(input: string): Color {
if (isColor(input)) {
return input;
}
throw new Error(`Invalid color: ${input}`);
}
枚举编译问题
// 如果枚举值需要用在计算属性中,不能使用 const enum
const enum Size {
Small = 1,
Medium,
Large
}
// ❌ 错误:const enum 在运行时不存在
const sizes = [Size.Small, Size.Medium, Size.Large];
// ✅ 改用普通 enum
enum Size {
Small = 1,
Medium,
Large
}
总结对比表
| 特性 | 数值枚举 | 字符串枚举 | const enum |
|---|---|---|---|
| 反向映射 | ✅ 有 | ❌ 无 | ✅ 编译时移除 |
| 运行时存在 | ✅ 是 | ✅ 是 | ❌ 否 |
| 可读性 | 一般 | 优秀 | 一般 |
| 性能 | 普通 | 普通 | 优秀 |
| 使用场景 | 标志位、状态码 | 配置、错误码 | 性能敏感场景 |
选择建议:
- 需要反向映射 → 数值枚举
- 需要可读性和调试方便 → 字符串枚举
- 性能优先,编译后不需要枚举 → const enum
- 需要位运算 → 数值枚举
枚举中的计算成员和常量成员
TypeScript 枚举中的计算成员和常量成员
1. 基本概念
枚举成员分为两种:
- 常量成员:在编译阶段就能确定值
- 计算成员:在运行时才能确定值
enum Example {
// 常量成员
A, // 0(默认)
B = 1, // 1(字面量)
C = 1 + 2, // 3(常量表达式)
D = 1 << 2, // 4(位运算)
// 计算成员
E = Math.random(), // 运行时计算
F = "hello".length, // 运行时计算
G = parseInt("123") // 运行时计算
}
2. 常量成员详解
2.1 隐式常量成员
enum Direction {
Up, // 0(第一个成员,默认0)
Down, // 1(前一个成员+1)
Left, // 2
Right // 3
}
// 编译后的 JavaScript
var Direction;
(function (Direction) {
Direction[Direction["Up"] = 0] = "Up";
Direction[Direction["Down"] = 1] = "Down";
Direction[Direction["Left"] = 2] = "Left";
Direction[Direction["Right"] = 3] = "Right";
})(Direction || (Direction = {}));
2.2 显式常量成员
enum Status {
Pending = 10, // 显式设置为10
InProgress, // 11(自动递增)
Completed = 20, // 显式设置为20
Cancelled // 21(自动递增)
}
2.3 常量表达式成员
enum Flags {
// 这些都是常量成员(编译时计算)
A = 1 + 2, // 3
B = 3 * 4, // 12
C = 1 << 3, // 8(位运算)
D = 0b1010, // 10(二进制)
E = 0o12, // 10(八进制)
F = 0xA, // 10(十六进制)
G = ~1, // -2(位运算取反)
H = 10 | 5, // 15(位运算或)
}
3. 计算成员详解
3.1 什么是计算成员
计算成员的值在运行时才能确定:
enum ComputedExample {
// 这些都是在运行时计算的
Length = "hello".length, // 5
Random = Math.random(), // 随机数
Timestamp = Date.now(), // 当前时间戳
Parsed = parseInt("100px"), // 100
PI = Math.PI, // 3.141592653589793
MAX = Number.MAX_SAFE_INTEGER // 9007199254740991
}
3.2 计算成员的约束
enum MixedEnum {
// 常量成员
A = 1,
B = A + 1, // ✅ 可以引用之前的常量成员
// 计算成员
C = "text".length, // 4
// ❌ 错误:计算成员后的成员必须显式初始化
D, // Error: Enum member must have initializer
// ✅ 必须显式初始化
E = C + 1, // 5(计算成员)
F = 10 // 10(常量成员)
}
4. 常量 vs 计算成员的判断规则
判断流程图
成员初始化值
↓
是否字面量(数字/字符串)? → 是 → 常量成员
↓ 否
是否一元运算符? → 是 → 常量成员
↓ 否
是否二元运算符? → 是 → 两边都是常量? → 是 → 常量成员
↓ 否 ↓ 否
是否是特定全局对象? → 是 → 计算成员
↓ 否
是否是特定函数调用? → 是 → 计算成员
↓ 否
其他情况 → 计算成员
具体判断规则
enum Test {
// ✅ 常量成员(字面量)
A = 1,
B = "hello",
// ✅ 常量成员(常量表达式)
C = 1 + 2,
D = -1, // 一元运算符
E = ~0, // 位运算取反
F = 1 << 3, // 位运算
G = 2 ** 3, // 幂运算
// ✅ 常量成员(引用之前的常量成员)
H = A + 1, // 2
I = B.length, // 5(字符串字面量的属性访问)
// ❌ 计算成员(运行时计算)
J = "hello".length, // 字符串对象方法调用
K = Math.random(), // 数学函数调用
L = Date.now(), // 日期函数调用
M = parseInt("123"), // 全局函数调用
// ❌ 计算成员后的成员必须初始化
// N, // 错误!
O = 10 // 必须显式初始化
}
5. 实际应用场景
场景1:配置相关枚举
enum ApiConfig {
// 常量成员
TIMEOUT = 5000,
RETRY_TIMES = 3,
// 计算成员(基于常量)
TOTAL_TIMEOUT = TIMEOUT * RETRY_TIMES, // 15000
// 字符串计算
BASE_URL = "https://api.example.com",
AUTH_ENDPOINT = BASE_URL + "/auth" // ✅ 常量表达式
}
enum DynamicConfig {
// 运行时配置
CURRENT_YEAR = new Date().getFullYear(), // 计算成员
RANDOM_SEED = Math.floor(Math.random() * 1000),
// ⚠️ 后面的成员必须显式初始化
NEXT_YEAR = CURRENT_YEAR + 1
}
场景2:权限标志位
enum Permission {
// 常量成员(位标志)
NONE = 0,
READ = 1 << 0, // 1
WRITE = 1 << 1, // 2
DELETE = 1 << 2, // 4
EXECUTE = 1 << 3, // 8
// 计算成员(组合权限)
READ_WRITE = READ | WRITE, // 3
ALL = READ | WRITE | DELETE | EXECUTE, // 15
// 动态权限(基于环境)
IS_ADMIN = process.env.IS_ADMIN === 'true' ? ALL : READ // 计算成员
}
场景3:状态管理
enum LoadingState {
// 常量成员
IDLE = "idle",
LOADING = "loading",
// 计算成员(基于环境或配置)
TIMEOUT_DURATION = parseInt(process.env.TIMEOUT || "30000"),
// 错误状态(动态生成)
ERROR_PREFIX = "error_",
NETWORK_ERROR = ERROR_PREFIX + "network", // "error_network"
SERVER_ERROR = ERROR_PREFIX + "server" // "error_server"
}
6. 重要注意事项
6.1 const enum 的限制
// ❌ 错误:const enum 不能有计算成员
const enum BadEnum {
A = "hello".length // Error: const enum member initializers must be constant expressions
}
// ✅ 正确:只包含常量成员
const enum GoodEnum {
A = 1,
B = A + 1, // ✅ 引用常量成员
C = 1 << 2 // ✅ 常量表达式
}
6.2 计算成员后的初始化
enum Example {
A = 1,
B = "test".length, // 计算成员
// C, // ❌ 错误:必须初始化
D = 10, // ✅ 显式初始化
E = A + D // ✅ 11(引用前面的常量成员)
}
6.3 类型安全
enum Mixed {
NumberVal = 1,
StringVal = "hello",
ComputedVal = Math.random()
}
// 类型推断
type ValueType = typeof Mixed[keyof typeof Mixed];
// 相当于:1 | "hello" | number
function handleValue(val: ValueType) {
// val 可以是 1、"hello" 或任意 number
}
7. 性能考虑
编译时优化
// 常量成员在编译时被替换
enum Constants {
PI = 3.14159,
E = 2.71828,
ANSWER = 42
}
// 编译后:直接使用字面量
const area = 3.14159 * radius * radius; // PI 被替换
// 计算成员在运行时计算
enum RuntimeValues {
RANDOM = Math.random(),
TIMESTAMP = Date.now()
}
// 编译后:保留函数调用
const random = Math.random(); // 每次调用都重新计算
8. 最佳实践
8.1 明确区分用途
// ✅ 清晰的设计
enum AppConstants {
// 配置常量(编译时确定)
VERSION = "1.0.0",
MAX_ITEMS = 100,
TIMEOUT_MS = 5000,
// 计算值(运行时确定)
START_TIME = Date.now(),
RANDOM_ID = Math.random().toString(36).substr(2),
// 基于配置的计算
API_URL = process.env.API_URL || "http://localhost:3000"
}
8.2 避免混用模式
// ❌ 避免:混用模式导致困惑
enum Confusing {
A = 1,
B = "string",
C = A + 2, // 数字计算
D = B + " suffix", // 字符串拼接
E = Math.random() // 随机数
}
// ✅ 清晰:分开定义
enum NumericValues {
DEFAULT_TIMEOUT = 3000,
MAX_RETRIES = 3,
BACKOFF_MULTIPLIER = 2
}
enum StringValues {
STATUS_SUCCESS = "success",
STATUS_ERROR = "error",
STATUS_PENDING = "pending"
}
enum RuntimeValues {
CURRENT_TIMESTAMP = Date.now(),
SESSION_ID = generateSessionId()
}
9. 调试技巧
enum DebugExample {
STATIC_VALUE = 100,
COMPUTED_VALUE = Math.random(),
// 添加调试信息
DEBUG_INFO = (() => {
console.log("枚举初始化时计算");
return "debug";
})()
}
// 查看枚举结构
console.log(DebugExample);
console.log(Object.keys(DebugExample));
console.log(Object.values(DebugExample));
// 类型检查
type Keys = keyof typeof DebugExample; // "STATIC_VALUE" | "COMPUTED_VALUE" | "DEBUG_INFO"
type Values = typeof DebugExample[Keys]; // 100 | number | "debug"
总结
| 特性 | 常量成员 | 计算成员 |
|---|---|---|
| 确定时机 | 编译时 | 运行时 |
| 允许的操作 | 字面量、常量表达式、位运算 | 函数调用、方法调用、变量引用 |
| const enum | ✅ 支持 | ❌ 不支持 |
| 性能 | 编译时优化,运行时无开销 | 运行时计算,有性能开销 |
| 反向映射 | ✅ 数值枚举支持 | ❌ 不支持 |
| 自动递增 | ✅ 支持 | ❌ 不支持 |
| 初始化要求 | 无特殊要求 | 之后的成员必须显式初始化 |
使用建议:
- 优先使用常量成员,除非必要才用计算成员
- 需要性能优化时使用 const enum(只含常量成员)
- 计算成员后一定要显式初始化后续成员
- 避免在计算成员中使用复杂的运行时逻辑