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

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 的类型系统是你的“设计伙伴”——它不仅能避免运行时错误,更能通过类型推导引导你写出更优雅的代码。

最新发布