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

相关推荐
a cool fish(无名)23 分钟前
rust-参考与借用
java·前端·rust
只有干货1 小时前
前端传字符串 后端比较date类型字段
前端
波波鱼દ ᵕ̈ ૩2 小时前
学习:JS[6]环境对象+回调函数+事件流+事件委托+其他事件+元素尺寸位置
前端·javascript·学习
太阳上的雨天2 小时前
与 TRON (波场) 区块链进行交互的命令行工具 (CLI): tstroncli
typescript·区块链·交互·tron·trx·trc20
climber11212 小时前
【Python Web】一文搞懂Flask框架:从入门到实战的完整指南
前端·python·flask
Watermelo6172 小时前
极致的灵活度满足工程美学:用Vue Flow绘制一个完美流程图
前端·javascript·vue.js·数据挖掘·数据分析·流程图·数据可视化
门前云梦3 小时前
ollama+open-webui本地部署自己的模型到d盘+两种open-webui部署方式(详细步骤+大量贴图)
前端·经验分享·笔记·语言模型·node.js·github·pip
Micro麦可乐3 小时前
前端拖拽排序实现详解:从原理到实践 - 附完整代码
前端·javascript·html5·拖拽排序·drop api·拖拽api
Watermelo6173 小时前
Web Worker:让前端飞起来的隐形引擎
前端·javascript·vue.js·数据挖掘·数据分析·node.js·es6
Micro麦可乐3 小时前
前端与 Spring Boot 后端无感 Token 刷新 - 从原理到全栈实践
前端·spring boot·后端·jwt·refresh token·无感token刷新