在 TypeScript 的类型系统中,字面量类型(Literal Types)是最精确的类型表示形式,它不仅能指定变量是什么类型,还能指定变量必须是什么具体值。这种精确性为开发带来了强大的类型约束能力。
什么是字面量类型?
字面量类型允许开发者将具体的值直接作为类型使用,而不是范围更广的类型如 string 或 number。TypeScript 支持多种字面量类型:
// 字符串字面量类型
type Direction = "north" | "east" | "south" | "west";
// 数字字面量类型
type DiceRoll = 1 | 2 | 3 | 4 | 5 | 6;
// 布尔字面量类型
type TrueOnly = true;
四种字面量类型详解
graph TD
A[字面量类型] --> B[字符串字面量]
A --> C[数字字面量]
A --> D[布尔字面量]
A --> E[模版字面量]
B --> F[定义固定字符串集合]
C --> G[定义固定数值集合]
D --> H[true/false精确区分]
E --> I[组合字符串模式]
1. 字符串字面量类型(String Literal Types)
最常用的字面量类型,用于约束变量为特定字符串:
type HttpMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
function fetchData(url: string, method: HttpMethod) {
// ...
}
// 正确用法
fetchData("/users", "GET");
// 错误用法
fetchData("/users", "get"); // 错误:'get' 不属于 HttpMethod 类型
2. 数字字面量类型(Numeric Literal Types)
适用于需要精确数值约束的场景:
type Month = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
type Rating = 1 | 2 | 3 | 4 | 5;
function setMonth(month: Month) {
console.log(`设置月份为:${month}`);
}
setMonth(5); // 正确
setMonth(13); // 错误:13 不在可选范围内
3. 布尔字面量类型(Boolean Literal Types)
精确区分 true 或 false,适用于需要严格布尔值的场景:
function enableFeature(isEnabled: true) {
console.log("功能已启用");
}
function disableFeature(isDisabled: false) {
console.log("功能已禁用");
}
enableFeature(true); // 正确
enableFeature(false); // 错误:只能传入 true
disableFeature(false); // 正确
disableFeature(true); // 错误:只能传入 false
4. 模板字面量类型(Template Literal Types)
TypeScript 4.1+ 引入,提供动态生成字符串字面量类型的能力:
type StatusCode = `HTTP_${200 | 400 | 404 | 500}`;
// "HTTP_200" | "HTTP_400" | "HTTP_404" | "HTTP_500"
type CSSValue = `${number}px` | `${number}em` | `${number}rem`;
// "10px" | "2em" | "1.5rem" 等格式
function setPadding(padding: CSSValue) {
// ...
}
setPadding("15px"); // 正确
setPadding("2.5rem"); // 正确
setPadding("10vh"); // 错误:'vh' 不被支持
字面量类型的核心应用场景
graph TD
J[主要应用] --> K[Discriminated Unions]
J --> L[函数重载]
J --> M[配置对象]
J --> N[API响应处理]
1. 可辨识联合(Discriminated Unions)
通过共享字面量类型实现类型安全的联合类型:
type SuccessResponse = {
status: "success";
data: object;
};
type ErrorResponse = {
status: "error";
errorCode: number;
message: string;
};
type ApiResponse = SuccessResponse | ErrorResponse;
function handleResponse(response: ApiResponse) {
// 根据字面量类型进行区分
if (response.status === "success") {
console.log("数据:", response.data);
} else {
console.error(`错误 ${response.errorCode}: ${response.message}`);
}
}
2. 类型安全的配置对象
避免魔法字符串问题:
type Theme = "dark" | "light" | "system";
type FontSize = "small" | "medium" | "large";
interface AppConfig {
theme: Theme;
fontSize: FontSize;
animate: boolean;
}
function createApp(config: AppConfig) {
// 使用安全配置
}
// 正确配置
createApp({
theme: "dark",
fontSize: "medium",
animate: true
});
// 错误示范
createApp({
theme: "dark-mode", // 错误:不在 Theme 选项中
fontSize: "medium",
animate: true
});
3. 函数重载与参数验证
type Operation = "add" | "subtract" | "multiply" | "divide";
function calculate(operation: Operation, a: number, b: number): number {
switch (operation) {
case "add": return a + b;
case "subtract": return a - b;
case "multiply": return a * b;
case "divide": return a / b;
default:
// 由于TypeScript的类型保护,default分支实际上不会执行
const _exhaustiveCheck: never = operation;
return _exhaustiveCheck;
}
}
console.log(calculate("add", 5, 3)); // 8
console.log(calculate("power", 2, 3)); // 错误:'power' 不在选项中
4. API端点与路由安全
type ApiEndpoint =
| "/user/login"
| "/user/profile"
| "/posts/all"
| `/posts/${number}`;
function fetchApi(endpoint: ApiEndpoint) {
// ...
}
fetchApi("/user/profile"); // 正确
fetchApi("/posts/42"); // 正确
fetchApi("/comments/recent"); // 错误:不在允许的端点中
高级技巧
graph TD
O[高级技巧] --> P[const断言]
O --> Q[类型收缩]
O --> R[模版字面量类型]
O --> S[实用类型操作]
1. const 断言固定变量类型
使用 as const 将变量声明为不可变字面量类型:
// 普通数组类型:(string | number)[]
const mixedValues = ["admin", "user", 1];
// 字面量类型数组:readonly ["admin", "user", 1]
const userRoles = ["admin", "user", 1] as const;
// 自动推导为 "admin" 类型,而不是 string
const adminRole = userRoles[0];
2. 类型收缩与类型守卫
在条件语句中自动收缩类型范围:
function processMessage(message: string | boolean) {
if (message === true) {
// 此处 message 为 true 类型
console.log("真值消息");
} else if (message === false) {
// 此处 message 为 false 类型
console.log("假值消息");
} else {
// 此处 message 为 string 类型
console.log(`字符串消息:${message}`);
}
}
3. 实用类型操作
使用内置类型操作符处理字面量类型:
// 从对象提取字面量类型
const statuses = {
IDLE: "idle",
LOADING: "loading",
SUCCESS: "success",
ERROR: "error"
} as const;
type Status = keyof typeof statuses;
// "IDLE" | "LOADING" | "SUCCESS" | "ERROR"
// 使用模板字面量重写键名
type EventTypes = "click" | "hover" | "focus";
type EventHandlers = `on${Capitalize<EventTypes>}`;
// "onClick" | "onHover" | "onFocus"
// 从字面量类型移除前缀
type EventType = Uncapitalize<RemovePrefix<EventHandlers, 'on'>>;
// "click" | "hover" | "focus"
// 辅助类型:移除前缀
type RemovePrefix<T extends string, P extends string> =
T extends `${P}${infer Rest}` ? Rest : T;
4. 复杂模板字面量模式
创建高级字符串验证规则:
// Email验证类型简例
type EmailLocalPart = `${string}@${string}.${string}`;
type Email = `${string}@${string}.${"com" | "org" | "net"}`;
// 路由参数提取
type RouteParams<T extends string> =
T extends `${string}/:${infer Param}/${infer Rest}`
? Param | RouteParams<`/${Rest}`>
: T extends `${string}/:${infer Param}`
? Param
: never;
type Params = RouteParams<"/user/:userId/post/:postId">;
// "userId" | "postId"
// CSS选择器验证
type CSSSelector =
| ElementSelector
| ClassSelector
| IdSelector
| AttributeSelector;
type ElementSelector = `${string}`;
type ClassSelector = `.${string}`;
type IdSelector = `#${string}`;
type AttributeSelector = `[${string}]`;
字面量类型 VS 枚举类型
| 特性 | 字面量联合类型 | 枚举类型 |
|---|---|---|
| 类型定义 | type Size = 'S' | 'M' | 'L' | enum Size { S, M, L } |
| 运行时值 | 保留原始值 | 生成额外运行时代码 |
| 反向映射 | 不支持 | 支持 |
| 字符串值枚举 | 原生支持 | 需要显示设置 Size.S = 'S' |
| 编译后大小 | 无额外代码 | 生成额外运行时代码 |
| 树摇优化 (Tree Shaking) | 支持良好 | 完全支持需要额外配置 |
| 复杂值支持 | 支持数字、布尔值、字符串 | 仅支持数值和字符串 |
推荐选择:
- 使用字符串字面量联合类型处理简单选项值
- 仅当需要反向映射时才使用枚举类型
- 对于外部API合约,考虑使用
as const对象实现类似枚举的结构:
const Size = {
SMALL: "S",
MEDIUM: "M",
LARGE: "L"
} as const;
type Size = typeof Size[keyof typeof Size]; // "S" | "M" | "L"
案例分析:实现类型安全的国际化
// 1. 定义支持的语言
type Language = "en" | "es" | "fr" | "de";
// 2. 定义所有可能的消息键
type MessageKey = "welcome" | "goodbye" | "warning";
// 3. 定义消息结构
type Messages = {
[lang in Language]: {
[key in MessageKey]: string;
};
};
// 4. 实现消息字典
const messages: Messages = {
en: {
welcome: "Welcome!",
goodbye: "Goodbye!",
warning: "Warning!"
},
es: {
welcome: "¡Bienvenido!",
goodbye: "¡Adiós!",
warning: "¡Advertencia!"
},
fr: {
welcome: "Bienvenue!",
goodbye: "Au revoir!",
warning: "Avertissement!"
},
de: {
welcome: "Willkommen!",
goodbye: "Auf Wiedersehen!",
warning: "Warnung!"
}
};
// 5. 类型安全的翻译函数
function translate(lang: Language, key: MessageKey): string {
return messages[lang][key];
}
// 使用
console.log(translate("es", "welcome")); // "¡Bienvenido!"
console.log(translate("jp", "welcome")); // 错误:"jp" 不在 Language 中
console.log(translate("en", "intro")); // 错误:"intro" 不在 MessageKey 中
为什么使用字面量类型?
- 提高类型安全性:消除魔法字符串/数字,防止无效值
- 提升代码可读性:明确表达允许的值集合
- 增强IDE支持:提供精确的自动补全提示
- 改善重构能力:类型检查保证值改变时的正确性
- 精确错误定位:在编译时而非运行时发现问题
当与 TypeScript 的其他高级特性(如联合类型、类型守卫和类型推断)结合使用时,字面量类型成为创建精确、自描述和类型安全代码的不可缺少的工具。