【TS系列】TypeScript进阶(一)

前言

前面我们已经介绍了TypeScript的介绍以及TypeScript的安装和配置、TypeScript的基本类型👏👏
今天我们接着前面讲述TypeScript的进阶内容~👏👏
如果文章有什么需要改进的地方还请大佬不吝赐教👏👏
在此先感谢各位大佬啦~~🤞🤞
在这里插入图片描述

一、类型守卫

类型保护是可执行运行时检查的一种表达式,用于确保该类型在一定的范围内。换句话说,类型保护可以保证一个字符串是一个字符串,尽管它的值也可以是一个数值。类型保护与特性检测并不是完全不同,其主要思想是尝试检测属性、方法或原型,以确定如何处理值。目前主要有四种的方式来实现类型保护:

1. in 关键字

interface Admin {
  name: string;
  privileges: string[];
}

interface Employee {
  name: string;
  startDate: Date;
}

type UnknownEmployee = Employee | Admin;

function printEmployeeInformation(emp: UnknownEmployee) {
  console.log("Name: " + emp.name);
  if ("privileges" in emp) {
    console.log("Privileges: " + emp.privileges);
  }
  if ("startDate" in emp) {
    console.log("Start Date: " + emp.startDate);
  }
}

2. typeof 关键字

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}'.`);
}

typeof 类型保护只支持两种形式:typeof v === "typename"typeof v !== typename"typename" 必须是 "number""string""boolean""symbol"。 但是 TypeScript 并不会阻止你与其它字符串比较,语言不会把那些表达式识别为类型保护。

3. instanceof 关键字

interface Padder {
  getPaddingString(): string;
}

class SpaceRepeatingPadder implements Padder {
  constructor(private numSpaces: number) {}
  getPaddingString() {
    return Array(this.numSpaces + 1).join(" ");
  }
}

class StringPadder implements Padder {
  constructor(private value: string) {}
  getPaddingString() {
    return this.value;
  }
}

let padder: Padder = new SpaceRepeatingPadder(6);

if (padder instanceof SpaceRepeatingPadder) {
  // padder的类型收窄为 'SpaceRepeatingPadder'
}

4. 自定义类型保护的类型谓词

function isNumber(x: any): x is number {
  return typeof x === "number";
}

function isString(x: any): x is string {
  return typeof x === "string";
}

二、联合类型和类型别名

1. 联合类型

联合类型通常与 nullundefined 一起使用:

const sayHello = (name: string | undefined) => {
  /* ... */
};

例如,这里 name 的类型是 string | undefined 意味着可以将 stringundefined 的值传递给sayHello 函数。

sayHello("Semlinker");
sayHello(undefined);

通过这个示例,你可以凭直觉知道类型 A 和类型 B 联合后的类型是同时接受 A 和 B 值的类型。

2. 可辨识联合

TypeScript 可辨识联合(Discriminated Unions)类型,也称为代数数据类型或标签联合类型。它包含 3 个要点:可辨识、联合类型和类型守卫。

这种类型的本质是结合联合类型和字面量类型的一种类型保护方法。如果一个类型是多个类型的联合类型,且多个类型含有一个公共属性,那么就可以利用这个公共属性,来创建不同的类型保护区块。

1.可辨识

可辨识要求联合类型中的每个元素都含有一个单例类型属性,比如:

enum CarTransmission {
  Automatic = 200,
  Manual = 300
}

interface Motorcycle {
  vType: "motorcycle"; // discriminant
  make: number; // year
}

interface Car {
  vType: "car"; // discriminant
  transmission: CarTransmission
}

interface Truck {
  vType: "truck"; // discriminant
  capacity: number; // in tons
}

在上述代码中,我们分别定义了 MotorcycleCarTruck 三个接口,在这些接口中都包含一个 vType 属性,该属性被称为可辨识的属性,而其它的属性只跟特性的接口相关。

2.联合类型

基于前面定义了三个接口,我们可以创建一个 Vehicle 联合类型:

type Vehicle = Motorcycle | Car | Truck;

现在我们就可以开始使用 Vehicle 联合类型,对于 Vehicle 类型的变量,它可以表示不同类型的车辆。

3.类型守卫

下面我们来定义一个 evaluatePrice 方法,该方法用于根据车辆的类型、容量和评估因子来计算价格,具体实现如下:

const EVALUATION_FACTOR = Math.PI; 
function evaluatePrice(vehicle: Vehicle) {
  return vehicle.capacity * EVALUATION_FACTOR;
}

const myTruck: Truck = { vType: "truck", capacity: 9.5 };
evaluatePrice(myTruck);

对于以上代码,TypeScript 编译器将会提示以下错误信息:

Property 'capacity' does not exist on type 'Vehicle'.
Property 'capacity' does not exist on type 'Motorcycle'.

原因是在 Motorcycle 接口中,并不存在 capacity 属性,而对于 Car 接口来说,它也不存在 capacity 属性。那么,现在我们应该如何解决以上问题呢?这时,我们可以使用类型守卫。下面我们来重构一下前面定义的 evaluatePrice 方法,重构后的代码如下:

function evaluatePrice(vehicle: Vehicle) {
  switch(vehicle.vType) {
    case "car":
      return vehicle.transmission * EVALUATION_FACTOR;
    case "truck":
      return vehicle.capacity * EVALUATION_FACTOR;
    case "motorcycle":
      return vehicle.make * EVALUATION_FACTOR;
  }
}

在以上代码中,我们使用 switchcase 运算符来实现类型守卫,从而确保在 evaluatePrice 方法中,我们可以安全地访问 vehicle 对象中的所包含的属性,来正确的计算该车辆类型所对应的价格。

3. 类型别名

类型别名用来给一个类型起个新名字。

type Message = string | string[];

let greet = (message: Message) => {
  // ...
};

三、交叉类型

TypeScript 交叉类型是将多个类型合并为一个类型。 这让我们可以把现有的多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性。

interface IPerson {
  id: string;
  age: number;
}

interface IWorker {
  companyId: string;
}

type IStaff = IPerson & IWorker;

const staff: IStaff = {
  id: 'E1006',
  age: 33,
  companyId: 'EFT'
};

console.dir(staff)

在上面示例中,我们首先为 IPerson 和 IWorker 类型定义了不同的成员,然后通过 & 运算符定义了 IStaff 交叉类型,所以该类型同时拥有 IPerson 和 IWorker 这两种类型的成员。

小结

以上就是关于TypeScript的进阶内容介绍,参考资料写出,如果侵权,请联系删除!~
后续将会围绕TypeScript相关内容及其知识点不定期持续更新,感谢一路有你们的关注和陪伴!
(若有错误,请批评改正,谢谢~)

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小石Sir.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值