TypeScript 中的联合类型(Union Types)详解
联合类型 是 TypeScript 类型系统中最强大的特性之一,使用 |(竖线)将多个类型组合起来,表示一个值可以是几种类型中的任意一种。
1. 基本语法与用法
let id: string | number = 123; // 可以是 string 或 number
id = "abc123"; // OK
// id = true; // 错误:boolean 不属于联合类型
常见基础联合类型:
let flag: boolean | null = true;
flag = null; // OK
let status: "loading" | "success" | "error" = "loading";
status = "success"; // OK
// status = "failed"; // 错误:不是这三个值之一
2. 类型缩小(Type Narrowing)—— 联合类型的核心优势
TypeScript 会根据条件判断自动缩小变量的类型范围,提供更精确的类型检查和智能提示。
function printId(id: string | number) {
// 这里 id 是 string | number
if (typeof id === "string") {
// TS 自动缩小:这里 id 是 string
console.log(id.toUpperCase()); // 安全调用字符串方法
console.log(id.length);
} else {
// TS 自动缩小:这里 id 是 number
console.log(id.toFixed(2)); // 安全调用数字方法
}
}
printId(123); // 调用数字分支
printId("hello"); // 调用字符串分支
其他常见类型守卫(Type Guards)方式:
function process(value: string | null | number) {
if (value === null) {
// value 被缩小为 null
return;
}
if (typeof value === "string") {
// value 被缩小为 string
value.toLowerCase();
} else {
// value 被缩小为 number
value.toPrecision(2);
}
}
typeof检查原始类型(string/number/boolean/symbol/function)===/!==检查字面量或 null/undefined- 自定义类型守卫函数:
function isString(value: any): value is string {
return typeof value === "string";
}
function log(value: string | number) {
if (isString(value)) {
// value 被缩小为 string
console.log(value.repeat(2));
}
}
3. 与其他类型的组合
a. 联合类型 + 数组
let mixedArray: (string | number)[] = [1, "two", 3, "four"];
mixedArray.push(5); // OK
mixedArray.push("six"); // OK
// mixedArray.push(true); // 错误
b. 联合类型 + 对象属性
interface Success {
type: "success";
data: string;
}
interface Error {
type: "error";
message: string;
}
type Result = Success | Error; // 可辨识联合(Discriminated Union)
function handleResult(result: Result) {
if (result.type === "success") {
// result 被缩小为 Success
console.log(result.data.toUpperCase());
} else {
// result 被缩小为 Error
console.log("错误:" + result.message);
}
}
可辨识联合 是联合类型的最佳实践模式:通过一个共同的字面量属性(tag,如 type、kind)来区分不同分支。
4. 联合类型与函数
// 参数为联合类型
function format(value: string | number): string {
return typeof value === "number" ? value.toFixed(2) : value.trim();
}
// 返回值为联合类型
function getStatus(): "ok" | "failed" | null {
return Math.random() > 0.5 ? "ok" : "failed";
}
5. 常见内置联合类型
string | null | undefined:常用于可选值(开启strictNullChecks时)HTMLElement | null:如document.getElementById()any可以看作是所有类型的联合(但不推荐使用)
6. 注意事项
- 联合类型的方法限制:只能调用所有类型共有的方法。
let value: string | number = "hello";
// value.toUpperCase(); // 错误:number 上没有这个方法
value.toString(); // OK:string 和 number 都有 toString()
- 使用类型守卫 来安全访问特定类型的方法。
7. 最佳实践建议
| 场景 | 推荐用法 |
|---|---|
| 可能为空的值 | string | null | undefined + ?? 或 ?. |
| 状态机 | "idle" | "loading" | "success" | "error" |
| API 响应 | 可辨识联合(带 type 或 kind 字段) |
| 多类型参数 | 联合类型 + 类型守卫函数 |
| 避免过度宽松 | 尽量用具体字面量联合,而不是 string | number |
小结:联合类型速查
| 写法 | 含义 | 示例场景 |
|---|---|---|
string | number | ID 可以是字符串或数字 | 用户ID、订单号 |
"left" | "right" | "center" | 固定字符串选项 | 对齐方式、方向 |
Success | Error | 可辨识联合 | API 响应处理 |
T | null | undefined | 可空类型 | DOM 元素、配置值 |
联合类型是 TypeScript 类型安全的核心,配合类型缩小和可辨识联合,能大幅减少运行时错误,提升代码可维护性。
如果您想深入了解交叉类型(Intersection Types)、联合类型与泛型的结合、或条件类型(Conditional Types),请告诉我!