阅读 124

构建 Typescript 知识体系(七)-高级类型之交叉类型与联合类型

这是我参与更文挑战的第十三天,活动详情查看:更文挑战

TS 的高级类型是,TS 为了保证语言的灵活性,所引用的一些语言特性。这些特性有利于应对复杂多变的开发场景

什么是交叉类型

将多个类型合并为一个类型,新的类型将具有所有类型的特性,所以交叉类型特别适合对象过多的场景。交叉类型实际上是取所有类型的并集

interface DogInterface {
  run(): void;
}

interface CatInterface {
  jump(): void;
}

// pet变量将具有两个接口类型的所有方法
/*
不能将类型“{}”分配给类型“DogInterface & CatInterface”。
  类型 "{}" 中缺少属性 "run",但类型 "DogInterface" 中需要该属性。ts(2322)
*/
let pet: DogInterface & CatInterface = {};
复制代码
interface DogInterface {
  run(): void;
}

interface CatInterface {
  jump(): void;
}

// pet变量将具有两个接口类型的所有方法
let pet: DogInterface & CatInterface = {
  run() {},
  jump() {},
};
复制代码

什么是联合类型

声明的类型并不确定,可以为多个类型中的一个

let a: number | string = "a";
let b: number | string = 1;
复制代码

有时候不仅要限制变量的类型,还需要限定变量的取值在某个特定的范围内

字符串的字面量联合类型

//  testName是一个联合类型,并且取值必须是   'a'|'b'|'c' 中的一个
let testName: "a" | "b" | "c";
复制代码

数字的字面量联合类型

let testName: 1 | 2 | 3;
复制代码

对象的联合类型

如果对象时联合类型,在对象不确定的情况下,只能访问所有类型的公有有成员( 这种情况下的联合类型只能取所有成员的交集)

interface DogInterface {
  run(): void;
}

interface CatInterface {
  jump(): void;
}

// pet变量将具有两个接口类型的所有方法
let pet: DogInterface & CatInterface = {
  run() {},
  jump() {},
};

class Dog implements DogInterface {
  run() {}
  eat() {}
}

class Cat implements CatInterface {
  jump() {}
  eat() {}
}

enum Master {
  Boy,
  Girl,
}

function getPet(master: Master) {
  // 类型推断为:  (let pet: Dog | Cat)  联合类型
  let pet = master === Master.Boy ? new Dog() : new Cat();
  // 编译器提示只能调用  eat 方法
  pet.eat();
  return pet;
}
复制代码

可区分的联合类型

本质上是结合联合类型与字面量类型的一种类型保护方法。核心思想:一个类型如果是多个对象的联合类型,并且每个类型之间有一个公共属性,就可以凭借此公共属性,创建类型保护区块

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

interface Reactangle {
  kind: "reactangle";
  width: number;
  height: number;
}

type Shape = Square | Reactangle;

function area(s: Shape) {
  // 通过两个类型的共有属性 kind,就可以创建类型保护区块
  switch (s.kind) {
    case "reactangle":
      return s.height * s.width;
    case "square":
      return s.size * s.size;
  }
}
复制代码

上面的代码如果不升级维护是没有问题的,但是如果添加了一个新的形状后呢?

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

interface Reactangle {
  kind: "reactangle";
  width: number;
  height: number;
}

interface Circle {
  kind: "circle";
  r: number;
}

type Shape = Square | Reactangle | Circle;

function area(s: Shape) {
  switch (s.kind) {
    case "reactangle":
      return s.height * s.width;
    case "square":
      return s.size * s.size;
  }
}

// 打印出undefined
console.log(area({ kind: "circle", r: 100 }));
复制代码

解决方式一:为函数指定一个明确的返回类型

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

interface Reactangle {
  kind: "reactangle";
  width: number;
  height: number;
}

interface Circle {
  kind: "circle";
  r: number;
}

type Shape = Square | Reactangle | Circle;
/*
函数缺少结束 return 语句,返回类型不包括 "undefined"。ts(2366)
*/
function area(s: Shape): number {
  switch (s.kind) {
    case "reactangle":
      return s.height * s.width;
    case "square":
      return s.size * s.size;
  }
}

console.log(area({ kind: "circle", r: 100 }));
复制代码

解决方式二: 检查是否是 never 类型,如果是 never 类型,说明前面所有的分支都被覆盖了, 如实 s 不是 never 类型,就说明前面的分支有遗漏

interface Reactangle {
  kind: "reactangle";
  width: number;
  height: number;
}

interface Circle {
  kind: "circle";
  r: number;
}

type Shape = Square | Reactangle | Circle;

function area(s: Shape) {
  switch (s.kind) {
    case "reactangle":
      return s.height * s.width;
    case "square":
      return s.size * s.size;
    default:
      /*
类型“Circle”的参数不能赋给类型“never”的参数。ts(2345)
*/
      return ((e: never) => {
        throw new Error(e);
      })(s);
  }
}

console.log(area({ kind: "circle", r: 100 }));
复制代码
interface Square {
  kind: "square";
  size: number;
}

interface Reactangle {
  kind: "reactangle";
  width: number;
  height: number;
}

interface Circle {
  kind: "circle";
  r: number;
}

type Shape = Square | Reactangle | Circle;

function area(s: Shape) {
  switch (s.kind) {
    case "reactangle":
      return s.height * s.width;
    case "square":
      return s.size * s.size;
    // 添加上遗漏的分支即可
    case "circle":
      return Math.PI * s.r ** 2;
    default:
      return ((e: never) => {
        throw new Error(e);
      })(s);
  }
}

// 打印出正确的值
console.log(area({ kind: "circle", r: 100 }));
复制代码
文章分类
前端
文章标签