写代码时经常碰到这种情况:状态用 0、1、2 表示,过两周回来看 if (status === 2) 完全想不起来 2 是什么。这种散落在代码里的裸数字叫「魔法数字」,而枚举(enum)就是 TypeScript 提供的解药——给一组相关常量起上名字。

数字枚举:默认从 0 开始

用 enum 关键字定义枚举,成员之间用逗号分隔。不手动赋值时,第一个成员从 0 开始自动编号。

enum Status {
  Draft,      // 0
  Review,     // 1
  Published,  // 2
}

let post: Status = Status.Published;
console.log(post);              // 2
console.log(Status.Review);     // 1

// 用枚举代替魔法数字,语义一眼就懂
function canEdit(status: Status): boolean {
  return status === Status.Draft;
}
console.log(canEdit(Status.Draft));   // true
console.log(canEdit(Status.Review));  // false

枚举既是类型也是值:可以像类型一样标注变量,也可以像对象一样取成员。

手动赋值与反向映射

数字枚举支持手动赋值,未赋值的成员会接着上一个值继续递增。数字枚举还有个特别的能力——反向映射,能从值反查名字。

enum Level {
  Low = 1,
  Middle,     // 自动变成 2
  High = 10,
  Max,        // 自动变成 11
}

console.log(Level.Middle);   // 2
console.log(Level.Max);      // 11

// 反向映射:值 -> 名字
console.log(Level[2]);       // 'Middle'
console.log(Level[10]);      // 'High'

反向映射在调试和日志里很实用:拿到接口返回的 2,用 Level[2] 就能打出 'Middle'。

字符串枚举

成员也可以赋字符串值,这叫字符串枚举。它没有反向映射,但调试时打印出来的是可读的名字,而不是一个数字,实际项目里往往更受欢迎。

enum Direction {
  Up = "UP",
  Down = "DOWN",
  Left = "LEFT",
  Right = "RIGHT",
}

function move(dir: Direction): string {
  return `向 ${dir} 移动`;
}

console.log(move(Direction.Up));    // 向 UP 移动
console.log(Direction.Down);        // DOWN
// console.log(Direction["UP"]);    // 编译报错:字符串枚举没有反向映射

经验法则:需要序列化、要打印进日志的用字符串枚举;需要做数学运算或节省体积的用数字枚举。

枚举成员也能当类型用

枚举的每个成员本身就是一个类型,函数参数可以限定成「只接受某一个成员」。

enum Color {
  Red = "RED",
  Green = "GREEN",
  Blue = "BLUE",
}

// 只接受 Color.Red 这一个值
function toHex(c: Color.Red): string {
  return c === Color.Red ? "#ff0000" : "";
}

console.log(toHex(Color.Red));  // '#ff0000'
// toHex(Color.Blue);           // 编译报错:参数只能是 Color.Red

枚举与字面量联合类型怎么选

TypeScript 里还有另一种写法:字符串字面量联合类型。它更轻量,编译后直接消失;枚举则会生成真实的运行时对象。简单说:需要在运行时拿到这组值(遍历、反查、传给 JS 代码)就选枚举,只在类型层面约束就用字面量联合。

// 字面量联合:纯类型层面的约束,编译后不存在
type Dir = "up" | "down";
let d: Dir = "up";    // 只能是这两个字符串

小结

  • enum 给一组常量起名字,专治魔法数字;数字枚举默认从 0 自增
  • 数字枚举支持反向映射 Level[2],字符串枚举没有但日志可读性更好
  • 枚举成员本身也是类型,可以把参数收窄到单个成员
  • 需要运行时对象选枚举,纯类型约束选字面量联合类型,按需选择