TypeScript 联合类型(超详细)
💡一则或许对你有用的小广告
欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
- 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于
Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...
,点击查看项目介绍 ;- 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;
截止目前, 星球 内专栏累计输出 82w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 2900+ 小伙伴加入学习 ,欢迎点击围观
在现代前端开发中,TypeScript 通过静态类型系统显著提升了代码的健壮性和可维护性。而 TypeScript 联合类型(Union Types)作为其核心特性之一,允许变量同时接受多种类型值,为开发者提供了灵活且安全的类型定义方式。无论是处理动态数据结构、编写多态函数,还是应对复杂业务逻辑,联合类型都能帮助开发者更高效地管理类型边界。本文将从基础概念到实战应用,逐步解析联合类型的使用场景与进阶技巧,帮助读者掌握这一重要工具。
什么是联合类型?
联合类型允许一个变量同时接受多种类型值,其语法通过 |
符号连接多个类型表示。例如:
let id: number | string;
id = 123; // 合法
id = "user_456"; // 合法
通过联合类型,我们可以明确表达变量可能的值域,同时避免类型断言带来的安全隐患。
联合类型 vs 交叉类型
联合类型与交叉类型(Intersection Types)容易混淆,但两者逻辑相反:
- 联合类型:
A | B
表示“可能是 A 或 B”; - 交叉类型:
A & B
表示“同时具备 A 和 B 的特性”。
比喻:如果联合类型是“一个人可以是工程师或音乐家”,那么交叉类型则是“一个人既是工程师又是音乐家”。
联合类型的典型应用场景
1. 处理动态数据
当数据源可能返回多种类型时,联合类型能清晰表达类型边界。例如,API 返回的用户 ID 可能是数字或字符串:
interface User {
id: number | string;
name: string;
}
const user1: User = { id: 123, name: "Alice" }; // 合法
const user2: User = { id: "abc", name: "Bob" }; // 合法
2. 函数参数的多态性
函数可以接受不同类型的参数,联合类型能统一参数类型定义:
function logData(value: number | string | boolean) {
console.log(`Value is: ${value}`);
}
logData(42); // 合法
logData("Hello"); // 合法
logData(true); // 合法
3. 可选参数的默认值
当参数可选且需要默认值时,联合类型可与 undefined
结合使用:
function createItem(id: number | undefined = 0) {
return { id: id || "default" }; // 需处理可能的 undefined
}
联合类型的进阶用法
1. 类型守卫(Type Guards)
直接操作联合类型变量时,TypeScript 会限制访问其属性,因为无法确定具体类型。此时需通过 类型守卫 狭窄类型范围。常见方法包括:
(1) typeof
或 instanceof
function printValue(value: number | string) {
if (typeof value === "string") {
console.log(value.toUpperCase()); // 确认是 string 类型
} else {
console.log(value * 2); // 确认是 number 类型
}
}
(2) 用户自定义类型守卫
通过返回布尔值的函数实现更复杂的类型判断:
type Dog = { species: "dog"; bark: () => void };
type Cat = { species: "cat"; meow: () => void };
function isDog(animal: Dog | Cat): animal is Dog {
return animal.species === "dog";
}
function handleAnimal(animal: Dog | Cat) {
if (isDog(animal)) {
animal.bark(); // 确认是 Dog 类型
} else {
animal.meow(); // 确认是 Cat 类型
}
}
2. 联合类型的解构与嵌套
在对象或数组中,联合类型可以嵌套使用:
// 对象属性的联合类型
interface ApiResponse {
status: "success" | "error";
data: object | null;
message: string | undefined;
}
// 数组元素的联合类型
const mixedList: (number | string)[] = [1, "two", 3];
3. 联合类型的默认值与可选属性
结合可选属性(?
)和默认值,能更灵活地定义接口:
interface Settings {
theme: "light" | "dark";
fontSize?: number | "auto"; // 可选且接受 number 或 "auto"
}
常见问题与解决方案
1. 未穷举所有类型导致的错误
若未处理所有可能的联合类型分支,TypeScript 会报错。例如:
function checkStatus(status: "online" | "offline" | "away") {
if (status === "online") {
console.log("User is online");
} else if (status === "offline") {
console.log("User is offline");
}
// 报错:未处理 "away" 类型
}
解决方案:添加 else
分支或使用 else if (status === "away")
。
2. 联合类型过于宽泛导致的误用
若联合类型包含过多类型(如 any
),会失去类型检查的作用。例如:
// 错误示例:类型过于宽泛
let value: string | number | boolean | null | undefined;
最佳实践:保持联合类型的最小必要范围,避免使用 any
或过度泛化类型。
3. 联合类型与函数重载的结合
通过函数重载,可以为不同参数类型提供不同的返回类型:
function processInput(input: number): string;
function processInput(input: string): number;
function processInput(input: number | string): number | string {
if (typeof input === "number") {
return input.toString();
} else {
return parseInt(input);
}
}
实战案例:构建类型安全的表单验证器
假设我们需要验证用户输入的邮箱或电话号码,联合类型可帮助我们统一定义规则:
type FormField = "email" | "phone";
interface ValidationRules {
[key: string]: {
type: FormField;
required: boolean;
minLength?: number;
};
}
const rules: ValidationRules = {
emailField: {
type: "email",
required: true,
minLength: 5,
},
phoneField: {
type: "phone",
required: true,
},
};
function validateInput(fieldName: keyof ValidationRules, value: string): boolean {
const rule = rules[fieldName];
if (!rule.required && !value) return true;
switch (rule.type) {
case "email":
return /^[^@]+@[^.]+\..+$/.test(value);
case "phone":
return /^\d{10}$/.test(value);
default:
return false; // 确保穷举所有类型
}
}
此案例中,联合类型 FormField
明确了字段类型,而 ValidationRules
接口通过联合类型约束规则定义,使代码更易维护。
结论
TypeScript 联合类型为开发者提供了在类型安全与灵活性之间取得平衡的工具。通过合理使用联合类型,我们能更清晰地定义动态数据结构、增强函数多态性,并通过类型守卫等技巧保障代码的健壮性。对于初学者而言,建议从基础语法开始,逐步结合类型守卫和函数重载等高级特性进行实践;中级开发者则可探索联合类型在复杂场景(如 API 响应处理)中的深度应用。掌握联合类型后,你将更自信地构建可扩展、低 Bug 的大型项目。
记住,TypeScript 的类型系统是你的“设计伙伴”——它不仅能避免运行时错误,更能通过类型推导引导你写出更优雅的代码。