PS:文章参考了满神的B站TS教程
满神主页

TS 交叉类型

交叉类型将多个类型合并为一个类型,新类型包含所有类型的所有属性,使用 & 符号连接。

1. 交叉类型的定义与基本语法

1.1 基本语法

/**
 * TypeScript 交叉类型语法
 * type 类型1 = { ... }
 * type 类型2 = { ... }
 * type 新类型 = 类型1 & 类型2;
 */
type PersonType = {
  name: string;
  age: number;
};

type WorkType = {
  job: string;
  salary: number;
};

type Employee = PersonType & WorkType;

const employee: Employee = {
  name: '张三',
  age: 18,
  job: '前端工程师',
  salary: 10000,
};

1.2 交叉类型的特点

  • 交叉类型使用 & 符号连接多个类型
  • 新类型必须包含所有被交叉类型的所有属性
  • 交叉类型创建的是一个新的类型,而不是联合类型

2. 交叉类型的使用场景

2.1 组合接口或类型

当需要一个对象同时具备多个接口的所有特性时,可以使用交叉类型

interface BirdType {
  fly(): void;
  layEggs(): void;
}

interface FishType {
  swim(): void;
  layEggs(): void;
}

type PetType = BirdType & FishType;

const petpet: PetType = {
  fly() {
    console.log('我会飞');
  },
  layEggs() {
    console.log('我会下蛋');
  },
  swim() {
    console.log('我会游泳');
  },
};

2.2 混入(Mixins)模式

  • 交叉类型常用于实现混入模式,将多个类或接口的功能组合到一个新类型中
  • 这种模式在需要多重继承功能时特别有用
// 定义基础能力
type Flyable = {
  fly(): void;
  altitude: number;
};

type Swimmable = {
  swim(): void;
  depth: number;
};

type Walkable = {
  walk(): void;
  speed: number;
};

// 组合多种能力
type SuperAnimal = Flyable & Swimmable & Walkable;

const superAnimal: SuperAnimal = {
  fly() {
    console.log(`飞行高度: ${this.altitude}`);
  },
  altitude: 1000,
  swim() {
    console.log(`游泳深度: ${this.depth}`);
  },
  depth: 50,
  walk() {
    console.log(`行走速度: ${this.speed}km/h`);
  },
  speed: 30,
};

2.3 扩展现有类型

// 基础用户类型
type BaseUser = {
  id: number;
  name: string;
  email: string;
};

// 权限类型
type Permissions = {
  canRead: boolean;
  canWrite: boolean;
  canDelete: boolean;
};

// 时间戳类型
type Timestamps = {
  createdAt: Date;
  updatedAt: Date;
};

// 完整的用户类型
type FullUser = BaseUser & Permissions & Timestamps;

const user: FullUser = {
  id: 1,
  name: '李四',
  email: 'lisi@example.com',
  canRead: true,
  canWrite: true,
  canDelete: false,
  createdAt: new Date(),
  updatedAt: new Date(),
};

3. 注意点:属性冲突处理

如果交叉类型中的两个类型有同名但类型不兼容的属性,交叉类型会变为 never

3.1 同名属性类型兼容

type PersonType2 = {
  name: string;
  age: number;
};

type WorkType2 = {
  name: string; // 同名且类型相同,可以正常合并
  salary: number;
};

type Employee2 = PersonType2 & WorkType2;

const employee2: Employee2 = {
  name: '王五', // 正常工作
  age: 25,
  salary: 15000,
};

3.2 同名属性类型冲突

type PersonType3 = {
  name: string;
  id: number; // id 是 number 类型
};

type WorkType3 = {
  name: string;
  id: string; // id 是 string 类型,与上面冲突
};

type Employee3 = PersonType3 & WorkType3;

// Error Code: 由于 id 属性类型冲突,Employee3 实际上变成了 never 类型
// const employee3: Employee3 = {
//   name: '赵六',
//   id: ???, // 无法同时满足 number 和 string 类型
// };

3.3 解决属性冲突的方法

// 方法1:使用联合类型解决冲突
type PersonType4 = {
  name: string;
  id: number | string; // 使用联合类型
};

type WorkType4 = {
  name: string;
  id: string;
};

type Employee4 = PersonType4 & WorkType4;

const employee4: Employee4 = {
  name: '孙七',
  id: 'EMP001', // 可以是字符串
};

// 方法2:重新设计类型结构,避免冲突
type PersonInfo = {
  name: string;
  personalId: number;
};

type WorkInfo = {
  name: string;
  workId: string;
};

type Employee5 = PersonInfo & WorkInfo;

const employee5: Employee5 = {
  name: '周八',
  personalId: 123456,
  workId: 'EMP002',
};

4. 交叉类型与联合类型的区别

交叉类型(&)要求同时满足所有类型,联合类型(|)只需满足其中一个类型

type A = {
  name: string;
  age: number;
};

type B = {
  job: string;
  salary: number;
};

// 交叉类型:必须同时具备 A 和 B 的所有属性
type IntersectionType = A & B;
const intersection: IntersectionType = {
  name: '张三',
  age: 30,
  job: '工程师',
  salary: 20000,
};

// 联合类型:只需要是 A 或 B 中的一种
type UnionType = A | B;
const union1: UnionType = {
  name: '李四',
  age: 25,
}; // 只需要满足 A 类型

const union2: UnionType = {
  job: '设计师',
  salary: 18000,
}; // 只需要满足 B 类型

5. 实际应用示例

5.1 API 响应类型组合

// 基础响应结构
type BaseResponse = {
  code: number;
  message: string;
  timestamp: number;
};

// 分页信息
type Pagination = {
  page: number;
  pageSize: number;
  total: number;
};

// 用户数据
type UserData = {
  data: {
    id: number;
    name: string;
    email: string;
  }[];
};

// 完整的用户列表响应类型
type UserListResponse = BaseResponse & Pagination & UserData;

const userListResponse: UserListResponse = {
  code: 200,
  message: 'success',
  timestamp: Date.now(),
  page: 1,
  pageSize: 10,
  total: 100,
  data: [
    { id: 1, name: '用户1', email: 'user1@example.com' },
    { id: 2, name: '用户2', email: 'user2@example.com' },
  ],
};

5.2 配置对象类型组合

// 数据库配置
type DatabaseConfig = {
  host: string;
  port: number;
  database: string;
};

// 认证配置
type AuthConfig = {
  username: string;
  password: string;
};

// 连接池配置
type PoolConfig = {
  min: number;
  max: number;
  timeout: number;
};

// 完整的数据库连接配置
type FullDatabaseConfig = DatabaseConfig & AuthConfig & PoolConfig;

const dbConfig: FullDatabaseConfig = {
  host: 'localhost',
  port: 3306,
  database: 'myapp',
  username: 'admin',
  password: 'password123',
  min: 5,
  max: 20,
  timeout: 30000,
};

创作不易,请多支持
写的会有差别,大家也可以参考:

下一章:TS联合类型

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐