当前位置: 首页 > news >正文

谈谈 TypeScript 中的联合类型(union types)和交叉类型(intersection types),它们的应用场景是什么?

一、联合类型(Union Types)

核心概念

使用管道符 | 表示多选一关系,典型场景:处理可能存在多种类型的变量

// 基础示例:处理数值型ID(number)或哈希型ID(string)
type EntityID = number | string;

function getEntity(id: EntityID) {
  // 类型守卫处理分支逻辑
  if (typeof id === 'number') {
    console.log(`Fetching by numeric ID: ${id.toFixed(2)}`);
  } else {
    console.log(`Fetching by hash ID: ${id.toUpperCase()}`);
  }
}

实战应用场景

1. 可辨识联合(Discriminated Unions)

通过公共字段实现精确类型推断,适用于状态机等场景

// 图形系统形状定义
type Shape = 
  | { kind: "circle"; radius: number }     // 圆形
  | { kind: "square"; size: number }       // 正方形
  | { kind: "rectangle"; width: number; height: number };  // 长方形

function calculateArea(shape: Shape): number {
  switch (shape.kind) {
    case "circle":
      return Math.PI * shape.radius ​** 2;  // 自动识别radius属性
    case "square":
      return shape.size ​** 2;             // 自动识别size属性
    case "rectangle":
      return shape.width * shape.height;  // 自动识别宽高属性
  }
}
2. 可选参数处理

与 undefined 联用实现可选参数类型定义

// 带缓存的读取函数
type CacheConfig = {
  maxAge: number;
  useLocalStorage?: boolean;
};

function readCache(key: string, config?: CacheConfig) {
  // 类型自动推断为 CacheConfig | undefined
  const effectiveConfig = config || { maxAge: 3600 };
}

开发建议

  1. 优先使用联合类型处理多态数据,比 any 类型更安全
  2. 结合类型守卫(Type Guards)​ 进行精确类型处理
  3. 避免过度宽泛的联合,如 string | number | boolean 建议重构为更具体的联合
  4. 使用类型别名(type)管理复杂联合类型,提升代码可维护性

二、交叉类型(Intersection Types)

核心概念

使用 & 符号实现类型合并,类似接口继承但更灵活

// 基础示例:合并用户基础信息与权限信息
type User = { 
  id: number;
  name: string;
};

type Permission = {
  canEdit: boolean;
  isAdmin: boolean;
};

type UserWithPermission = User & Permission;

const adminUser: UserWithPermission = {
  id: 1,
  name: "Alice",
  canEdit: true,
  isAdmin: true
};

实战应用场景

1. 混入模式(Mixin Pattern)

实现功能组合的最佳实践

// 可序列化能力混入
type Serializable = {
  serialize(): string;
};

class BaseModel {
  constructor(public id: number) {}
}

// 通过交叉类型实现混入
type SerializableModel = BaseModel & Serializable;

const userModel: SerializableModel = {
  ...new BaseModel(1),
  serialize() {
    return JSON.stringify(this);
  }
};
2. 复杂配置合并

合并多个配置类型生成完整配置

// 分阶段配置定义
type BaseConfig = {
  apiEndpoint: string;
  timeout: number;
};

type AuthConfig = {
  clientId: string;
  token: string;
};

type LoggingConfig = {
  logLevel: "debug" | "info";
};

// 生成完整配置类型
type AppConfig = BaseConfig & AuthConfig & LoggingConfig;

const config: AppConfig = {
  apiEndpoint: "/api",
  timeout: 5000,
  clientId: "web-app",
  token: "abc123",
  logLevel: "debug"
};

开发建议

  1. 优先用于对象类型合并,基础类型交叉会产生 never
  2. 替代多接口继承,解决 TS 接口不能多继承的问题
  3. 避免深度嵌套交叉,超过3层的交叉类型建议重构
  4. 与泛型结合 实现灵活的类型操作

三、关键差异与注意事项

类型特性对比

| 特性 | 联合类型(|) | 交叉类型(&) |
|---------------------|------------------------|-----------------------|
| 语义 | 逻辑"或"关系 | 逻辑"与"关系 |
| 适用场景 | 处理不同类型输入 | 合并类型特性 |
| 基础类型操作 | string | number 有效 | string & number => never |
| 对象类型合并 | 创建包含共有属性的类型 | 合并所有属性 |

常见问题处理

1. 类型收缩陷阱
// 危险操作:未进行类型检查直接访问属性
function getLength(input: string | string[]) {
  // 编译错误:未收缩类型时访问length不安全
  return input.length; // Error: Property 'length' does not exist on type 'string | string[]'
  
  // 正确做法:类型断言或类型守卫
  if (typeof input === 'string') return input.length;
  return input.length;
}
2. 交叉类型误用
// 危险操作:基本类型交叉产生 never
type Impossible = string & number;  // 类型为 never

// 实际影响
function handleValue(val: string & number) {
  console.log(val); // 该函数永远无法被调用
}

高级技巧

1. 类型守卫优化
// 自定义类型守卫
function isErrorResponse(
  response: SuccessResponse | ErrorResponse
): response is ErrorResponse {
  return (response as ErrorResponse).error !== undefined;
}

// 使用示例
if (isErrorResponse(res)) {
  console.error(res.error.message);
}
2. 类型分发处理
// 条件类型中的分发特性
type ExtractString<T> = T extends string ? T : never;

// 实际应用
type Result = ExtractString<"hello" | 42 | true>;  // 得到类型 "hello"

四、工程化实践建议

  1. 项目规范
// 建议:独立类型声明文件(types.d.ts)
export type APIResponse<T> = 
  | { status: 'success'; data: T }
  | { status: 'error'; code: number };
  1. 文档注释
/**
 * 用户身份联合类型
 * @typedef {AdminUser | NormalUser} UserRole
 * @property {boolean} AdminUser.isSuperAdmin - 超级管理员标识
 * @property {string} NormalUser.department - 所属部门
 */
type UserRole = AdminUser | NormalUser;
  1. 性能优化
// 避免过度使用交叉类型
// 不推荐:超过3层的交叉类型
type OverComplex = A & B & C & D;

// 推荐:使用接口继承重构
interface Combined extends A, B, C, D {}

总结

联合类型与交叉类型共同构成了TS类型系统的核心武器库。

联合类型(Union Types)擅长处理不确定性输入,交叉类型(Intersection Types)专注解决确定性的类型组合

掌握二者的本质区别与适用边界,配合类型守卫等辅助手段,能够大幅提升代码的类型安全性。

在实际项目中,建议将复杂类型操作限制在系统边界层(如API响应处理),保持核心业务逻辑的类型简洁性。

相关文章:

  • 代码随想录算法训练营第34天 | 62.不同路径 63. 不同路径 II 整数拆分 不同的二叉搜索树 (跳过)
  • linux(centos8)下编译ffmpeg
  • HCIA-PPP
  • 每天五分钟深度学习PyTorch:循环神经网络RNN的计算以及维度信息
  • 大数据 Spark 技术简介
  • TLSR8355F128芯片特色解析
  • Linux中的epoll简单使用案例
  • 视频转音频, 音频转文字
  • 通过socket实现文件上传和下载功能
  • 信息系统运行管理员教程5--信息系统数据资源维护
  • PAT甲级(Advanced Level) Practice 1023 Have Fun with Numbers
  • LeetCode 1005. K 次取反后最大化的数组和 java题解
  • C语言 —— 此去经年梦浪荡魂音 - 深入理解指针(卷二)
  • SpringBoot3+Druid+MybatisPlus多数据源支持,通过@DS注解配置Service/Mapper/Entity使用什么数据源
  • Windows11 新机开荒(二)电脑优化设置
  • C++ 类和对象 友元 内部类 this指针 默认成员函数 初始化列表……
  • Pandas DataFrame:数据分析的利器
  • 14 结构体
  • WebSocket和长轮询
  • 【操作系统】Ch6 文件系统
  • 【社论】个人破产探索,要守住“诚实而不幸”的底线
  • 北京“准80后”干部兰天跨省份调任新疆生态环境厅副厅长
  • “不为一时一事所惑,不为风高浪急所扰”——习近平主席对俄罗斯进行国事访问并出席纪念苏联伟大卫国战争胜利80周年庆典纪实
  • 姚洋将全职加盟上海财经大学,担任滴水湖高级金融学院院长
  • 巴西总统卢拉抵达北京
  • 重庆大学通报本科生发14篇SCI论文处理结果