联合类型与交叉类型

什么是联合类型?

联合类型(Union Types)是TypeScript中一个强大的特性,它允许你将多个类型组合成一个类型。使用联合类型,一个值可以是几种类型之一。

基本语法

typescript 复制代码
let value: string | number;
value = "hello";  // 正确
value = 42;       // 正确
value = true;     // 错误,布尔值不在联合类型中

实际应用场景

  1. 处理多种可能的输入类型

    typescript 复制代码
    function formatInput(input: string | number) {
      if (typeof input === "string") {
        return input.toUpperCase();
      }
      return input.toFixed(2);
    }
  2. 描述可能为null或undefined的值

    typescript 复制代码
    let maybeString: string | null = null;
    maybeString = "now it's a string";

什么是交叉类型?

交叉类型(Intersection Types)是将多个类型合并为一个类型,新类型将包含所有类型的特性。

基本语法

typescript 复制代码
interface Person {
  name: string;
  age: number;
}

interface Employee {
  company: string;
  position: string;
}

type EmployeePerson = Person & Employee;

const john: EmployeePerson = {
  name: "John",
  age: 30,
  company: "Tech Corp",
  position: "Developer"
};

实际应用场景

  1. 组合多个对象类型

    typescript 复制代码
    function extend<T, U>(first: T, second: U): T & U {
      return { ...first, ...second };
    }
  2. 混入(Mixin)模式

    typescript 复制代码
    class Disposable {
      dispose() {
        console.log("Disposing...");
      }
    }
    
    class Activatable {
      activate() {
        console.log("Activating...");
      }
    }
    
    type SmartObject = Disposable & Activatable;

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

特性 联合类型 (Union) 交叉类型 (Intersection)
语法 `A B`
含义 A或B A且B
可赋值性 只需满足其中一个 必须满足所有
类型推断 类型保护缩小范围 合并所有属性

高级用法

类型保护与联合类型

typescript 复制代码
function padLeft(value: string, padding: string | number) {
  if (typeof padding === "number") {
    return Array(padding + 1).join(" ") + value;
  }
  if (typeof padding === "string") {
    return padding + value;
  }
  throw new Error(`Expected string or number, got '${padding}'.`);
}

条件类型中的联合与交叉

typescript 复制代码
type NonNullable<T> = T extends null | undefined ? never : T;
type Diff<T, U> = T extends U ? never : T;
type Filter<T, U> = T extends U ? T : never;

常见问题与解决方案

  1. 属性访问问题

    typescript 复制代码
    interface Bird {
      fly(): void;
      layEggs(): void;
    }
    
    interface Fish {
      swim(): void;
      layEggs(): void;
    }
    
    function getSmallPet(): Fish | Bird {
      // ...
    }
    
    let pet = getSmallPet();
    pet.layEggs(); // 没问题,两个接口都有
    pet.swim();    // 错误,可能是Bird

    解决方案:使用类型断言或类型保护

    typescript 复制代码
    if ("swim" in pet) {
      pet.swim();
    }
  2. 交叉类型中的属性冲突

    typescript 复制代码
    type Conflict = { a: number } & { a: string };
    // a的类型变为 number & string,即 never

总结

联合类型和交叉类型是TypeScript类型系统中两个核心概念:

  • 联合类型提供了灵活性,允许一个值属于多种类型之一
  • 交叉类型提供了组合能力,可以创建包含多个类型特性的新类型

掌握这两种类型的使用方法,能够帮助你更精确地描述JavaScript代码中的复杂数据结构和行为,从而编写出更健壮、更易维护的TypeScript代码。