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)。

相关推荐
哀木9 分钟前
随笔之 react 接入 @xterm 的踩坑记录
前端
野生的程序媛24 分钟前
重生之我在学Vue--第13天 Vue 3 单元测试实战指南
前端·javascript·vue.js·单元测试
Aphasia31130 分钟前
简单介绍清除浮动解决高度塌陷的四种方法✍🏻
前端·css
二川bro1 小时前
TypeScript接口 interface 高级用法完全解析
javascript·typescript
Captaincc1 小时前
这款堪称编程界的“自动驾驶”利器,集开发、调试、提 PR、联调、部署于一体
前端·ai 编程
我是小七呦1 小时前
万字血书!TypeScript 完全指南
前端·typescript
simple丶2 小时前
Webpack 基础配置与懒加载
前端·架构
simple丶2 小时前
领域模型 模板引擎 dashboard应用列表及配置接口实现
前端·架构
冰夏之夜影2 小时前
【css酷炫效果】纯css实现液体按钮效果
前端·css·tensorflow
2 小时前
告别手写Codable!Swift宏库ZCMacro让序列化更轻松
前端