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

相关推荐
hboot几秒前
rust 全栈应用框架dioxus
前端·rust·全栈
我是仙女你信不信6 分钟前
生成pdf并下载
前端·javascript·vue.js
少糖研究所6 分钟前
记一次Web Worker的使用
前端·性能优化
乔乔不姓乔呀8 分钟前
pc 和大屏如何适配
前端
speedoooo19 分钟前
新晋前端框架技术:小程序容器与SuperApp构建
前端·小程序·前端框架·web app
vvilkim28 分钟前
React 组件类型详解:类组件 vs. 函数组件
前端·javascript·react.js
猫了个咪丶30 分钟前
一文教你搞懂如何消除异步函数的传染性
前端
JiangJiang30 分钟前
🧠 useMemo + memo + useContext 性能优化实战:从无感重渲染到丝滑体验
前端·react.js·面试
糖墨夕32 分钟前
【3】Threejs环境光、点光源、聚光灯等常见光源类型对比
前端·three.js·canvas
汪子熙32 分钟前
使用 Trae 快速开发能生成二维码的 SAP UI5 应用
前端·trae