首页 / TypeScript 入门教程 / 字面量类型

TypeScript 入门教程

字面量类型

本教程共 80 篇 · 第 16 篇 · 更新于 2026-08-10 · 约 10 分钟阅读

TypeScriptTypeScript 入门教程字面量类型as const模板字面量类型

本节目标:掌握字符串、数字、布尔三种字面量类型的用法,理解 constas const 对类型推断的影响,学会用联合字面量替代枚举,以及模板字面量类型的动态生成。

字面量类型是什么

前面我们学的类型——stringnumberboolean——是”宽泛类型”。string 可以是任何字符串,number 可以是任何数字。但有时候你需要的不是”任何”,而是”那几个”。

字面量类型(literal type)让类型缩小到具体的值。它告诉 TypeScript:“这个变量不只是一般的 string,它只能是这几个特定的字符串”。

// 宽泛类型:能接受任何字符串
let name: string = "小明";
name = "小红";  // ✅

// 字面量类型:只能接受特定值
let direction: "up" | "down" = "up";
// direction = "left";  // ❌ "left" 不在 "up" | "down" 中

字符串字面量类型

最常见也最实用的字面量类型。把变量限制为几个特定的字符串:

// 直接写在注解里
let status: "pending" | "active" | "completed";
status = "pending";   // ✅
// status = "done";   // ❌

// 单独抽成类型别名(推荐)
type Status = "pending" | "active" | "completed";

function updateStatus(newStatus: Status) {
  console.log(`状态更新为: ${newStatus}`);
}

updateStatus("active");    // ✅
// updateStatus("waiting"); // ❌

编辑器的自动补全会列出所有选项——你甚至不用记有哪些值。这在定义 API 参数、组件 props、配置选项时极其好用。

数字字面量类型

跟字符串字面量同理,不过是把数字锁死在特定值上:

// 骰子点数:只能是 1 到 6
type DiceRoll = 1 | 2 | 3 | 4 | 5 | 6;

let roll: DiceRoll = 4;   // ✅
// roll = 7;              // ❌ 7 不是有效的骰子点数

// HTTP 状态码
type SuccessCode = 200 | 201 | 204;
type ErrorCode = 400 | 401 | 403 | 404 | 500;
type StatusCode = SuccessCode | ErrorCode;

function checkStatus(code: StatusCode): string {
  if (code === 200) return "OK";
  if (code === 404) return "Not Found";
  return String(code);
}

数字字面量在需要把返回值限定为特定数值时很有用——比如比较函数返回 -1 | 0 | 1

function compare(a: number, b: number): -1 | 0 | 1 {
  if (a < b) return -1;
  if (a > b) return 1;
  return 0;
}

布尔字面量类型

布尔只有两个值,所以布尔字面量类型就是 truefalse。实际上 TypeScript 的 boolean 就是 true | false 的别名。

单独使用 truefalse 作为类型不常见,但它可以用于区分重载或限制返回:

// 这个函数只能返回 true
function alwaysTrue(): true {
  return true;
  // return false;  // ❌
}

// 跟字面量联合一起用
type Result = true | "success" | 200;

const 和 as const:让 TypeScript”看紧一点”

let 声明的变量推断为宽泛类型,用 const 声明的则推断为字面量类型。这个行为我们在 08 章提过,现在展开讲:

let name = "小明";     // 类型: string(宽泛)
const name2 = "小明";  // 类型: "小明"(精确)

let score = 95;        // 类型: number
const score2 = 95;     // 类型: 95

const 的类型收窄只对顶层有效。对象和数组的属性仍然是宽泛类型:

const user = {
  name: "小明",   // 类型: string(不是 "小明")
  age: 25         // 类型: number(不是 25)
};
// user.name 仍然是 string——你可以 user.name = "小红"(如果对象不是 const 的话)

要让整个对象/数组都变成只读的字面量类型,用 as const 断言

const user = {
  name: "小明",
  age: 25
} as const;

// user 的类型现在是:
// {
//   readonly name: "小明";
//   readonly age: 25;
// }

// user.name = "小红";  // ❌ 只读属性不能修改
// user.age = 30;       // ❌ 只读属性不能修改

as const 同时干三件事:

  1. 把属性类型收窄为字面量(string"小明"
  2. 给所有属性加上 readonly
  3. 把数组变为只读元组

数组用 as const 的效果:

const colors = ["red", "green", "blue"] as const;
// 类型: readonly ["red", "green", "blue"]

// colors[0] = "yellow";  // ❌ 只读,不能修改
// colors.push("purple");  // ❌ 没有 push 方法

这在需要精确类型时非常有用——比如把 as const 数组传给需要字面量联合类型的函数:

const methods = ["GET", "POST", "DELETE"] as const;
type Method = typeof methods[number];  // "GET" | "POST" | "DELETE"

联合字面量替代枚举

第 12 章提到了用联合字面量替代枚举。这里给一个完整对比:

// 枚举写法
enum Direction {
  Up = "UP",
  Down = "DOWN",
  Left = "LEFT",
  Right = "RIGHT"
}

// 联合字面量写法(推荐)
type Direction2 = "UP" | "DOWN" | "LEFT" | "RIGHT";

// 两种写法的使用感几乎一样
function move(dir: Direction2) {
  console.log(`移动方向: ${dir}`);
}

move("UP");    // ✅ 编辑器自动补全
// move("up"); // ❌ 大小写不对也会报错

联合字面量的优势已经在枚举那章说过:零运行时开销、更简洁、不需要 import。如果你的常量只在一个文件内用或者值本身足够说明含义,联合字面量是更好的选择。

模板字面量类型

TypeScript 4.1 引入了模板字面量类型,可以用模板字符串语法构建新的字符串类型。这在需要动态拼接字符串类型的场景中非常强大:

// 基础用法:拼接字面量
type EventName = `on${string}`;
let event: EventName = "onClick";    // ✅
// let event2: EventName = "click";  // ❌ 不以 "on" 开头

// 联合类型分发:组合多个值
type Direction = "north" | "south" | "east" | "west";
type Distance = "1km" | "5km" | "10km";
type Route = `${Direction}-${Distance}`;
// 自动生成所有组合:
// "north-1km" | "north-5km" | "north-10km" |
// "south-1km" | ...(共 12 种)

let myRoute: Route = "east-5km";  // ✅
// let badRoute: Route = "north-2km";  // ❌ 2km 不在 Distance 中

配合内置的字符串工具类型,可以做大小写转换:

// Capitalize: 首字母大写
type Greeting = "hello" | "goodbye";
type FormalGreeting = Capitalize<Greeting>;  // "Hello" | "Goodbye"

// Uppercase / Lowercase
type Shout = Uppercase<"hello">;  // "HELLO"
type Whisper = Lowercase<"HELLO">;  // "hello"

// 组合使用
type Method = "get" | "post" | "delete";
type HandlerName = `handle${Capitalize<Method>}`;
// "handleGet" | "handlePost" | "handleDelete"

模板字面量类型适合生成有规律的字符串类型——事件名、CSS 单位、API 路径等。但对于极其复杂的动态字符串,模板字面量类型会让编译变慢,适度使用。

可运行示例

// 字面量类型综合示例

// 1. 联合字面量替代枚举
type LogLevel = "debug" | "info" | "warn" | "error";

function log(level: LogLevel, message: string) {
  let prefix: string;
  switch (level) {
    case "debug": prefix = "🔍"; break;
    case "info":  prefix = "📝"; break;
    case "warn":  prefix = "⚠️"; break;
    case "error": prefix = "❌"; break;
  }
  console.log(`${prefix} [${level.toUpperCase()}] ${message}`);
}

log("info", "服务启动成功");
log("warn", "内存使用率超过 80%");

// 2. as const:深度只读
const CONFIG = {
  host: "localhost",
  port: 8080,
  env: "development"
} as const;

// CONFIG.port = 3000;  // ❌ 只读,不能改
console.log(`服务器: ${CONFIG.host}:${CONFIG.port} (${CONFIG.env})`);

// 3. as const 数组 → 联合类型
const SIZES = ["sm", "md", "lg"] as const;
type Size = typeof SIZES[number];  // "sm" | "md" | "lg"

function render(size: Size) {
  console.log(`渲染尺寸: ${size}`);
}

render("md");  // ✅
// render("xl");  // ❌

// 4. 模板字面量类型
type Prefix = "btn" | "input" | "card";
type Variant = "primary" | "secondary" | "danger";

// 自动生成 class 名称
type BEMClass = `${Prefix}--${Variant}`;
// "btn--primary" | "btn--secondary" | "btn--danger" |
// "input--primary" | ...

let className: BEMClass = "card--danger";
console.log(`CSS 类名: ${className}`);

输出:

📝 [INFO] 服务启动成功
⚠️ [WARN] 内存使用率超过 80%
服务器: localhost:8080 (development)
渲染尺寸: md
CSS 类名: card--danger

小结

字面量类型是 TypeScript 类型精确度的终极体现——从”这是个数字”细化到”这只能是 1、2 或 3”。联合字面量替代枚举是新项目的推荐做法,as const 让普通对象和数组获得只读的字面量类型,模板字面量类型则把字符串拼接带入了类型层面。到这里,基础类型部分全部结束。你已经掌握了 TypeScript 最核心的类型工具——从最简单的 string/numbernever/unknown,再到联合、交叉和字面量类型。接下来可以进入更高级的类型特性了。