TypeScript日常类型(9): Literal Types

字面量类型(Literal Types)

除了通用的 string 和 number 类型,我们还可以在类型位置上引用特定的字符串和数字。

可以这样理解:JavaScript 提供了不同的方式来声明变量,var 和 let 允许更改变量的值,而 const 则不允许。这种行为在 TypeScript 处理字面量类型时得到了体现。

ts 复制代码
let changingString = "Hello World";
changingString = "Olá Mundo";
// 由于 `changingString` 可以表示任何可能的字符串,
// 因此 TypeScript 在类型系统中将其描述为 string 类型
// changingString: string
      
const constantString = "Hello World";
// 由于 `constantString` 只能表示唯一的字符串 "Hello World",
// 因此它具有字面量类型的表示
// const constantString: "Hello World";

单独使用时,字面量类型并没有太大价值:

ts 复制代码
let x: "hello" = "hello";  
// OK
x = "hello";   
x = "howdy"; // ❌ 报错:类型 "howdy" 不能赋值给类型 "hello"

但如果将字面量类型组合成 联合类型(Union Types) ,就能表达更有意义的概念。例如,可以定义 仅接受特定值 的函数:

ts 复制代码
function printText(text: "hello" | "goodbye") {
  console.log(text);
}

printText("hello");   // ✅ OK
printText("goodbye"); // ✅ OK
printText("hi");      // ❌ 报错:类型 "hi" 不能赋值给类型 '"hello" | "goodbye"'

数值字面量类型的工作方式与字符串字面量类型相同:

ts 复制代码
function rollDice(dice: 1 | 2 | 3 | 4 | 5 | 6) {  
  console.log(`Rolled a ${dice}`);  
}  

rollDice(3); // ✅ OK  
rollDice(6); // ✅ OK  
rollDice(7); // ❌ 报错:类型 "7" 不能赋值给类型 "1 | 2 | 3 | 4 | 5 | 6"

当然,你可以将字面量类型与非字面量类型结合使用:

ts 复制代码
function sendMessage(message: string, priority: "low" | "medium" | "high") {  
  console.log(`Message: ${message}, Priority: ${priority}`);  
}  

sendMessage("System update available", "high");  // ✅ OK  
sendMessage("Meeting at 3 PM", "medium");       // ✅ OK  
sendMessage("Lunch time", "urgent");           // ❌ 报错:类型 "urgent" 不能赋值给类型 '"low" | "medium" | "high"'

还有一种字面量类型:布尔字面量类型(Boolean Literal Types) 。

布尔字面量类型只有两个可能的值,正如你所猜测的,它们分别是 true 和 false。实际上,boolean 类型只是 true | false 的一个 别名(alias) 。

ts 复制代码
function toggle(value: true | false) {  
  return value ? "ON" : "OFF";  
}  

console.log(toggle(true));  // ✅ 输出: "ON"  
console.log(toggle(false)); // ✅ 输出: "OFF"  

let flag: boolean = true;  // ✅ 这里的 boolean 其实是 true | false 的别名
flag = false;              // ✅ 仍然合法

字面量推断(Literal Inference)

当你使用对象初始化一个变量时,TypeScript 默认假设 该对象的属性值可能会在之后被更改。例如,下面的代码:

ts 复制代码
const obj = { counter: 0 }; // TypeScript 推断 obj.counter 的类型为 number

if (someCondition) {
  obj.counter = 1; // ✅ 合法,因为 1 也是 number 类型
}

TypeScript 并不会将 1 赋值给一个之前为 0 的字段视为错误。换句话说,obj.counter 必须具有 number 类型,而不是 0 类型,因为类型不仅决定了读取行为,还决定了写入行为。

这种情况也同样适用于字符串类型:

ts 复制代码
declare function handleRequest(url: string, method: "GET" | "POST"): void;

const req = { url: "https://example.com", method: "GET" };
handleRequest(req.url, req.method);  // ❌ -   Argument of type 'string' is not assignable to parameter of type '"GET" | "POST"'.

在上面的例子中,req.method 被推断为 string 类型,而不是字面量类型 "GET"。这是因为在创建 req 和调用 handleRequest 之间,代码可能会修改 req.method 的值,比如将它赋值为 "GUESS"。因此,TypeScript 认为此代码存在错误。

有两种方法可以解决这个问题:

  1. 你可以通过在任意位置添加类型断言来改变推断:
ts 复制代码
// 修改 1:
const req = { url: "https://example.com", method: "GET" as "GET" };

// 修改 2:
handleRequest(req.url, req.method as "GET");

修改 1 的意思是:"我打算让 req.method 永远保持字面量类型 "GET"",这会防止以后将 "GUESS" 等其他值赋给该字段。

修改 2 的意思是:"我知道由于其他原因,req.method 的值是 "GET""。这只是告诉 TypeScript,"尽管类型系统推断了它为其他类型,我确信它是 "GET"",因此不会进行进一步的类型检查。

  1. 你可以使用 as const 来将整个对象转换为字面量类型,从而确保 req 的所有属性都被推断为字面量类型,而不是更广泛的类型:
ts 复制代码
const req = { url: "https://example.com", method: "GET" } as const;
handleRequest(req.url, req.method);

as const 后缀类似于 const,但它作用于 类型系统,确保所有属性被分配为字面量类型,而不是更一般的类型(如 string 或 number)。

相关推荐
回眸&啤酒鸭5 分钟前
【回眸】OpenSwarm 多智能体协作系统实战指南
大数据·前端·人工智能
用户693717500138414 分钟前
2026,程序员的时代拐点到了
android·前端·后端
大龄秃头程序员17 分钟前
一次 iBeacon + BLE 无感解锁方案的实现记录
前端
小兔子32 分钟前
Python 的 GIL 与 free-threading:3.13 之后「去 GIL」走到哪一步了
前端
IT_陈寒38 分钟前
SpringBoot自动配置差点让我加班到凌晨
前端·人工智能·后端
guslegend2 小时前
脚手架原理与本地调试:从 bin 软链接到 npm link
前端·npm·node.js·脚手架·前端工程化
海码事务所2 小时前
Google Play 新个人开发者账号上架指南:12 人连续 14 天封闭测试怎么做?
前端
田威AI2 小时前
图片内文字翻译的规格:输入输出、保真、自动化、时间与费用
前端·计算机视觉
不可能片场3 小时前
命令行中文变问号 我用环境变量救了场
前端·electron
lerhxx3 小时前
AI 应用中的上下文管理 —— 从"上下文窗口"到"分层压缩"的工程实践
前端·javascript