返回 导航

其他

hangge.com

TypeScript - 数据类型详解4(字面量类型、枚举类型)

作者:hangge | 2026-08-03 08:45
    在前面的文章中,我介绍了 TypeScript 的对象类型、联合类型和类型别名等高级类型。本文将继续深入探讨 TypeScript 类型系统的另外两个重要特性:字面量类型枚举类型。字面量类型允许我们将类型精确限定为某个具体的值,非常适合用于约束变量的取值范围。枚举类型则是 TypeScript 独有的特性(不存在于 JavaScript 中),用于定义一组命名的常量。掌握这两种类型,可以让我们在开发中更好地表达业务逻辑,提高代码的可读性和可维护性。

一、字面量类型

1,基本概念

(1)在 TypeScript 中,除了常规的 stringnumberboolean 等类型外,我们还可以使用 字面量类型(Literal Type),将类型精确限定为某个具体的值。

(2)下面是字面量类型的基本使用示例:
提示:字面量类型的变量只能被赋值为该字面量本身。单独使用字面量类型通常没有太大意义,但当我们将多个字面量类型联合在一起时,就能获得非常有用的结果。
// "Hello World" 也可以作为一种类型,叫作字面量类型
let message: "Hello World" = "Hello World";

// 错误:Type '"coder"' is not assignable to type '"Hello World"'
// message = "coder";  // 只能赋值 "Hello World"

// 123 也可以作为一种类型
let num: 123 = 123;
// num = 456;  // 错误:不能赋其他值


2,联合字面量类型

(1)字面量类型的真正价值在于与联合类型结合使用,形成 联合字面量类型,用于限制变量的取值范围:
提示:联合字面量类型非常适合用于表示有限的选项集合,如对齐方式、状态码、用户角色等场景。相比使用 string 类型,它能在编译时就发现拼写错误。

(2)在下面的代码中,Alignment 类型由多个字面量类型联合形成,限制了 align 变量只能取 'left''right''center' 这三个值之一。
// 将多个字面量类型联合在一起,限制 align 的取值范围
type Alignment = 'left' | 'right' | 'center';

let align: Alignment = 'left';    // 正常
align = 'right';   // 正常
align = 'center';  // 正常

// 错误:Type '"bottom"' is not assignable to type 'Alignment'
// align = 'bottom';


3,字面量推理与 as const

(1)在实际开发中,TypeScript 会根据变量的声明方式自动推断类型。使用 const 声明的变量会被推断为具体的字面量类型,而不是宽泛的类型。

(2)但是,当对象属性需要作为字面量类型传递时,可能会遇到类型推断的问题。例如下面的代码报错是因为 options 对象进行自动类型推导时,推导出了 {url: string,method: string} 类型,无法将 string 类型赋值给字面量类型。
type Method = 'GET' | 'POST';

function request(url: string, method: Method) {
  console.log(`请求 ${url},方法:${method}`);
}

const options = {
  url: "https://www.example.com/api",
  method: "POST"
};

// 错误:Argument of type 'string' is not assignable to parameter of type 'Method'
// request(options.url, options.method);
// 原因:options.method 被推断为 string 类型,而不是 'POST' 字面量类型

(3)我们可以使用以下三种方式来解决该问题:
提示as const 断言会将对象或数组的所有属性变为只读的字面量类型,这是最简洁的解决方案。它不仅能解决类型推断问题,还能防止意外修改。
type Method = 'GET' | 'POST';

function request(url: string, method: Method) {
  console.log(`请求 ${url},方法:${method}`);
}

// ========== 方式一:使用类型断言 as ==========
const options1 = {
  url: "https://www.example.com/api",
  method: "POST"
};
request(options1.url, options1.method as Method);  // 正常

// ========== 方式二:为对象指定类型 ==========
type RequestOptions = {
  url: string;
  method: Method;
};

const options2: RequestOptions = {
  url: "https://www.example.com/api",
  method: "POST"  // 这里必须是 GET 或 POST
};
request(options2.url, options2.method);  // 正常

// ========== 方式三:使用 as const 断言(推荐)==========
const options3 = {
  url: "https://www.example.com/api",
  method: "POST"
} as const;  // 将对象的所有属性变为字面量类型

request(options3.url, options3.method);  // 正常

二、枚举类型

1,认识枚举

(1)枚举(Enum)TypeScript 独有的特性,不存在于 JavaScript 中。枚举用于将一组可能出现的值逐个列举出来,定义在一个类型中。
提示:枚举类型非常适合用于表示一组固定的选项,如方向、状态、颜色等。相比字面量联合类型,枚举提供了更好的代码组织和运行时支持。

(2)枚举类型使用 enum 关键字定义,下面代码使用 enum 关键字可以定义一个枚举类型,其中 LEFTRIGHT 是该枚举的两个成员。调用函数时传入枚举成员,可以获得良好的类型检查。
// 定义 Direction 枚举
enum Direction {
  LEFT,
  RIGHT
}

// 指定 direction 参数为 Direction 枚举类型
function turnDirection(direction: Direction) {
  switch (direction) {
    case Direction.LEFT:
      console.log("改变角色的方向向左");
      break;
    case Direction.RIGHT:
      console.log("改变角色的方向向右");
      break;
    default:
      // 使用 never 确保枚举的每个成员都被处理
      const _check: never = direction;
      break;
  }
}

// 使用枚举:调用函数时传入对应的枚举项
turnDirection(Direction.LEFT);   // 输出:改变角色的方向向左
turnDirection(Direction.RIGHT);  // 输出:改变角色的方向向右

2,数字枚举

(1)枚举类型的成员默认是有值的。对于 数字枚举,如果没有指定值,默认从 0 开始自增长:
enum Direction {
  LEFT,   // 默认值为 0
  RIGHT   // 默认值为 1
}

// 获取枚举成员的值
console.log(Direction.LEFT);   // 输出:0
console.log(Direction.RIGHT);  // 输出:1

(2)我们也可以为枚举成员指定自定义的数值:
enum Direction {
  LEFT = 100,  // 指定值为 100
  RIGHT       // 自动递增,值为 101
}

console.log(Direction.LEFT);   // 输出:100
console.log(Direction.RIGHT);  // 输出:101

(3)数字枚举还有一个重要的特性:反向映射。既可以通过名称获取值,也可以通过值获取名称:
注意:反向映射只适用于 数字枚举,字符串枚举不支持反向映射。这是因为在编译后的代码中,数字枚举会生成双向映射对象,而字符串枚举只会生成单向映射。
enum Direction {
  LEFT,   // 0
  RIGHT   // 1
}

// 正向映射:名称 -> 值
console.log(Direction.LEFT);      // 输出:0
console.log(Direction["LEFT"]);   // 输出:0

// 反向映射:值 -> 名称
console.log(Direction[0]);        // 输出:"LEFT"
console.log(Direction[1]);        // 输出:"RIGHT"

3,字符串枚举

(1)除了数字枚举,TypeScript 还支持 字符串枚举。字符串枚举的每个成员都必须显式指定字符串值:
提示:字符串枚举在调试时更加友好,因为它的值本身就是有意义的字符串,而不需要根据数字去查找对应的名称。在需要语义清晰的场景下,推荐使用字符串枚举。
enum Direction {
  LEFT = "LEFT",
  RIGHT = "RIGHT"
}

console.log(Direction.LEFT);   // 输出:"LEFT"
console.log(Direction.RIGHT);  // 输出:"RIGHT"

(2)字符串枚举和数字枚举可以混合使用,形成 异构枚举(但通常不推荐):
enum Direction {
  LEFT,           // 数字枚举,值为 0
  RIGHT = "RIGHT" // 字符串枚举
}

console.log(Direction.LEFT);   // 输出:0
console.log(Direction.RIGHT);  // 输出:"RIGHT"

4,常量枚举(const enum)

(1)在枚举定义前添加 const 关键字,可以定义 常量枚举。常量枚举在编译时会被内联,不会生成额外的运行时代码:
// 普通枚举
enum NormalEnum {
  A,
  B
}

// 常量枚举
const enum ConstEnum {
  A,
  B
}

// 使用枚举
let normal = NormalEnum.A;  // 编译后:var normal = NormalEnum.A;
let constVal = ConstEnum.A; // 编译后:var constVal = 0;(直接内联)

(2)常量枚举与普通枚举的区别如下:
  • 编译结果:常量枚举在编译阶段会被删除,枚举值直接内联到使用处;普通枚举会编译成实际的 JavaScript 对象。
  • 反向映射:常量枚举没有反向映射功能;普通数字枚举支持反向映射。
  • 性能:常量枚举访问时不需要属性查找,性能更好。
提示:在高性能场景下,推荐使用 const enum。但如果需要在运行时动态访问枚举值或使用反向映射,则必须使用普通枚举。
// 常量枚举示例
const enum Direction {
  UP,
  DOWN,
  LEFT,
  RIGHT
}

// 使用常量枚举
let directions = [Direction.UP, Direction.DOWN, Direction.LEFT, Direction.RIGHT];

// 编译后的 JavaScript 代码:
// var directions = [0 /* UP */, 1 /* DOWN */, 2 /* LEFT */, 3 /* RIGHT */];

5,枚举的最佳实践

(1)在使用枚举时,建议遵循以下最佳实践:
  • 状态管理:优先使用数字枚举,如订单状态、用户等级等。
  • 配置选项:采用字符串枚举,保证语义清晰,便于调试。
  • 性能优化:高频访问场景使用 const enum 减少运行时开销。
  • 避免混用:慎用异构枚举(数字和字符串混用),保持代码一致性。

(2)下面是一个综合示例,展示枚举在实际项目中的应用:
提示:掌握字面量类型和枚举类型,能让我们的代码更加精确和安全。字面量类型适合简单的值约束,枚举类型适合复杂的业务场景。根据实际需求选择合适的类型,是 TypeScript 开发的重要技能。
// 订单状态枚举(数字枚举,便于状态机判断)
enum OrderStatus {
  Pending = 0,
  Paid = 1,
  Shipped = 2,
  Completed = 3,
  Cancelled = 4
}

// 支付方式枚举(字符串枚举,语义清晰)
enum PaymentMethod {
  CreditCard = "CREDIT_CARD",
  WeChat = "WECHAT",
  Alipay = "ALIPAY"
}

// 日志级别(常量枚举,高性能)
const enum LogLevel {
  DEBUG = 0,
  INFO = 1,
  WARN = 2,
  ERROR = 3
}

// 使用示例
function processOrder(status: OrderStatus) {
  if (status === OrderStatus.Pending) {
    console.log("订单待支付");
  } else if (status >= OrderStatus.Paid) {
    console.log("订单已处理");
  }
}

processOrder(OrderStatus.Pending);
评论

全部评论(0)

回到顶部