Skip to content

交叉类型 (Intersection Types)

交叉类型是 TypeScript 中的一种强大特性,它允许你组合多个类型的属性和方法,创建一个新类型。这个新类型包含所有参与交叉的类型的成员。换句话说,如果你有两个或更多的类型,并且希望创建一个同时具有这些类型所有特性的类型,那么你可以使用交叉类型。

定义交叉类型

你可以使用 & 操作符来定义交叉类型。下面是一些基本的例子:

typescript
// 定义两个接口
interface Bird {
  fly: () => void;
}

interface Fish {
  swim: () => void;
}

// 使用 & 创建交叉类型
type FlyingFish = Bird & Fish;

// 实现交叉类型
const flyingFish: FlyingFish = {
  fly() {
    console.log("Flying...");
  },
  swim() {
    console.log("Swimming...");
  },
};

在这个例子中,FlyingFish 类型既包含了 Bird 接口的所有成员(即 fly 方法),也包含了 Fish 接口的所有成员(即 swim 方法)。因此,任何实现 FlyingFish 类型的对象都必须提供这两个方法。

交叉类型的属性访问

当处理交叉类型时,TypeScript 允许你访问所有被交叉类型的属性和方法,因为它们都被认为是该交叉类型的一部分。

typescript
flyingFish.fly(); // 输出: Flying...
flyingFish.swim(); // 输出: Swimming...

交叉类型与联合类型的对比

  • 联合类型 (|):表示一个值可以是几种类型中的任意一种。

    typescript
    type Pet = Bird | Fish;
  • 交叉类型 (&):表示一个值必须同时满足几种类型的条件。

    typescript
    type FlyingFish = Bird & Fish;

交叉类型的常见用法

  1. 扩展对象形状:你可以使用交叉类型来扩展已有类型的功能,而无需修改原始类型定义。

    typescript
    interface Square {
      width: number;
    }
    
    interface Rectangle extends Square {
      height: number;
    }
    
    // 或者使用交叉类型
    type ColoredSquare = Square & { color: string };
    
    const coloredSquare: ColoredSquare = {
      width: 10,
      color: "red",
    };
  2. 混入(Mixins):在面向对象编程中,混入是一种将多个类的行为组合到一起的技术。虽然 TypeScript 不直接支持多继承,但你可以通过交叉类型模拟混入。

    typescript
    class Movable {
      move() {
        console.log("Moving...");
      }
    }
    
    class Resizable {
      resize() {
        console.log("Resizing...");
      }
    }
    
    // 使用交叉类型模拟混入
    type MovableResizable = Movable & Resizable;
    
    function createMovableResizable(): MovableResizable {
      return {
        move: () => console.log("Moving..."),
        resize: () => console.log("Resizing..."),
      };
    }
    
    const obj = createMovableResizable();
    obj.move();
    obj.resize();
  3. 函数重载:交叉类型还可以用来描述函数签名之间的交集。

    typescript
    function padLeft(value: string, padding: string): string;
    function padLeft(value: string, padding: number): string;
    function padLeft(value: string, padding: string | number): string {
      if (typeof padding === "number") {
        return Array(padding + 1).join(" ") + value;
      }
      return padding + value;
    }
    
    // 使用交叉类型简化函数重载
    type PadFunction = ((value: string, padding: string) => string) &
      ((value: string, padding: number) => string);
    
    const pad: PadFunction = padLeft;
  4. 实用工具类型:交叉类型经常出现在 TypeScript 的内置实用工具类型中,如 Partial<T>Readonly<T> 等等。

    typescript
    type PartialPerson = Partial<{ name: string; age: number }>;

注意事项

  • 重复属性冲突:如果两个类型中有同名但不兼容的属性,TypeScript 编译器会报错。例如:

    typescript
    interface A {
      x: number;
    }
    
    interface B {
      x: string;
    }
    
    type AB = A & B; // 错误:Property 'x' has conflicting types in types 'A' and 'B'.
  • 可选属性:如果两个类型都有同名的可选属性,则交叉类型的结果也将是可选的。

    typescript
    interface A {
      x?: number;
    }
    
    interface B {
      x?: string;
    }
    
    type AB = A & B; // x 是可选的,并且是 number | string 类型

总结

交叉类型是 TypeScript 提供的一个强大工具,它允许你组合多个类型的属性和方法,创建一个新类型。理解交叉类型的工作原理及其与联合类型的区别,可以帮助你在编写代码时充分利用 TypeScript 的灵活性,同时保持良好的类型安全性。

Released under the MIT License.