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

TS联合类型

TS 联合类型是 TypeScript 中用于表示一个值可以是几种类型之一的重要特性

1. 联合类型的基本概念

1.1 联合类型的定义

联合类型允许变量、参数或返回值接受多种类型中的一种,使用竖线 | 分隔不同类型

语法:

let 变量名: 类型1 | 类型2 | 类型3;

示例:

// 联合类型的基本使用
let unionType: string | number;
unionType = 'hello';
unionType = 123;

// Error Code
// unionType = true; // Error: 不能将类型"boolean"分配给类型"string | number"

1.2 联合类型的使用场景

1.2.1 函数参数的多类型支持

function printId(id: number | string) {
  console.log('Your ID is: ' + id);
}

printId(101);
printId('202');

// Error Code
// printId(true); // Error: 类型"boolean"的参数不能赋给类型"string | number"的参数

1.2.2 处理可能为 null 或 undefined 的值

function printId2(id: number | string | null | undefined) {
  if (id === null || id === undefined) {
    console.log('No ID provided');
  } else {
    console.log('Your ID is: ' + id);
  }
}

printId2(101);
printId2('202');
printId2(null);
printId2(undefined);

2. 访问联合类型的属性

2.1 公共属性

可以直接访问联合类型中所有类型共有的属性或方法

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

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

function getSmallPet(): Fish | Bird {
  return {
    fly() {},
    layEggs() {},
  };
}

let pet = getSmallPet();
pet.layEggs(); // 正确,共有属性

// Error Code
// pet.swim(); // Error: 类型"Fish | Bird"上不存在属性"swim"

2.2 类型保护

需要通过类型检查来访问特定类型的独有属性

2.2.1 使用 typeof 进行类型保护

function printId3(id: number | string) {
  if (typeof id === 'string') {
    // 在这个分支中,TypeScript 知道 id 是 string 类型
    console.log(id.toUpperCase());
  } else {
    // 在这个分支中,TypeScript 知道 id 是 number 类型
    console.log(id.toFixed(2));
  }
}

2.2.2 使用 instanceof 进行类型保护

class Dog {
  bark() {
    console.log('Woof!');
  }
}

class Cat {
  meow() {
    console.log('Meow!');
  }
}

function makeSound(animal: Dog | Cat) {
  if (animal instanceof Dog) {
    animal.bark(); // TypeScript 知道这是 Dog 类型
  } else {
    animal.meow(); // TypeScript 知道这是 Cat 类型
  }
}

2.2.3 使用 in 操作符进行类型保护

function move(pet: Fish | Bird) {
  if ('swim' in pet) {
    pet.swim(); // TypeScript 知道这是 Fish 类型
  } else {
    pet.fly(); // TypeScript 知道这是 Bird 类型
  }
}

2.2.4 使用自定义类型保护函数

function isFish(pet: Fish | Bird): pet is Fish {
  return (pet as Fish).swim !== undefined;
}

function move2(pet: Fish | Bird) {
  if (isFish(pet)) {
    pet.swim(); // TypeScript 知道这是 Fish 类型
  } else {
    pet.fly(); // TypeScript 知道这是 Bird 类型
  }
}

3. 可辨识联合类型

可辨识联合类型是一种特殊的联合类型,通过共同的字面量属性来区分不同的类型

3.1 可辨识联合类型的定义

interface Square {
  kind: 'square';
  size: number;
}

interface Rectangle {
  kind: 'rectangle';
  width: number;
  height: number;
}

interface Circle {
  kind: 'circle';
  radius: number;
}

type Shape = Square | Rectangle | Circle;

3.2 可辨识联合类型的使用

function getArea(shape: Shape): number {
  switch (shape.kind) {
    case 'square':
      return shape.size * shape.size; // TypeScript 知道这是 Square 类型
    case 'rectangle':
      return shape.width * shape.height; // TypeScript 知道这是 Rectangle 类型
    case 'circle':
      return Math.PI * shape.radius ** 2; // TypeScript 知道这是 Circle 类型
    default:
      // 确保所有情况都被处理
      const _exhaustiveCheck: never = shape;
      return _exhaustiveCheck;
  }
}

4. 联合类型的注意事项

4.1 类型收窄

  • TypeScript 会根据代码的逻辑自动收窄联合类型的范围
  • 通过类型保护,TypeScript 能够推断出更具体的类型

4.2 never 类型的使用

  • 在 switch 语句的 default 分支中使用 never 类型可以确保所有情况都被处理
  • 如果有遗漏的情况,TypeScript 会在编译时报错

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

  • 联合类型 (|):表示值可以是几种类型之一
  • 交叉类型 (&):表示值必须同时满足多种类型的约束
// 联合类型:可以是 string 或 number
let union: string | number = 'hello';

// 交叉类型:必须同时具有两个接口的所有属性
interface A { name: string; }
interface B { age: number; }
let intersection: A & B = { name: 'Tom', age: 25 };

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

下一章:TS类型断言

Logo

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

更多推荐