TypeScript的进阶操作

34 阅读3分钟

类型保护

类型保护的作用是告诉编译器:“在这个 if​ 语句块里,我已经检查过了,这个变量一定是 string​ 类型,请允许我使用 string​ 的方法。”

这个过程在 TypeScript 中被称为 Type Narrowing (类型收窄)。

TypeScript 提供了多种方式来实现类型保护,由简入深主要分为以下几类:

  • typeof
  • instanceof
  • in
  • 自定义
  • 可辨识联合
typeof

这是最常见的方式,适用于 JavaScript 的基础数据类型。

  • 适用类型: string​, number​, boolean​, symbol​。
  • 注意: 对于 null​、Array​ 或自定义对象,typeof​ 都会返回 'object'​,所以它不能用来区分具体的对象类型。
function printId(id: number | string) {
  if (typeof id === "string") {
    // 在这个块中,TS 知道 id 是 string
    console.log(id.toUpperCase()); 
  } else {
    // TS 自动推断这里 id 一定是 number
    console.log(id.toFixed(2));
  }
}
instanceof

当你需要检查一个变量是否是某个类的实例时使用。它通过原型链进行检查。

  • 适用类型: 类(Class)、内置对象(如 Date​, RegExp​)。
class Dog {
  bark() { console.log("Woof!"); }
}

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

function interact(pet: Dog | Cat) {
  if (pet instanceof Dog) {
    // TS 收窄 pet 为 Dog 类型
    pet.bark();
  } else {
    // TS 收窄 pet 为 Cat 类型
    pet.meow();
  }
}
in

如果不同的对象类型可以通过“是否拥有某个属性”来区分,可以使用 in​。

  • 场景: 区分接口或纯对象结构。
interface Admin {
  name: string;
  privileges: string[];
}

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

function printEmployeeInfo(emp: Admin | Employee) {
  // 检查 'privileges' 属性是否存在于 emp 中
  if ("privileges" in emp) {
    // TS 确认 emp 是 Admin
    console.log("Privileges: " + emp.privileges);
  } else {
    // TS 确认 emp 是 Employee
    console.log("Start Date: " + emp.startDate);
  }
}
自定义

这是 TypeScript 最强大的功能之一。有些逻辑太复杂(例如校验一个深层嵌套的对象),TS 无法自动推断。这时我们可以编写一个返回类型为 parameter is Type​ 的函数。

  • 语法核心: arg is Type​ 。
interface Fish { swim: () => void; }
interface Bird { fly: () => void; }

// 自定义保护函数
// 只有当函数返回 true 时,TS 才会认为 pet 是 Fish
function isFish(pet: Fish | Bird): pet is Fish {
  return (pet as Fish).swim !== undefined;
}

function move(pet: Fish | Bird) {
  if (isFish(pet)) {
    pet.swim(); // TS 愉快地接受了
  } else {
    pet.fly();  // TS 推断剩下的是 Bird
  }
}
可辨识联合

这是处理复杂业务逻辑最优雅的方式。通过在每个接口中强制添加一个共同的字面量字段(通常叫 kind​ 或 type​),利用 switch​ 或 if​ 来实现绝对精确的收窄。

interface Circle {
  kind: "circle"; // 辨识特征
  radius: number;
}

interface Square {
  kind: "square"; // 辨识特征
  sideLength: number;
}

type Shape = Circle | Square;

function getArea(shape: Shape) {
  switch (shape.kind) {
    case "circle":
      // TS 知道这里只能是 Circle
      return Math.PI * shape.radius ** 2;
    case "square":
      // TS 知道这里只能是 Square
      return shape.sideLength ** 2;
  }
}

类型推导

TypeScript 的一大核心能力就是 不写类型也能自动推导类型。分为以下几种类型推导:

  • 基础类型推导
  • 上下文类型推导
  • infer
基础类型推导

一些基础类型,不需要写具体的类型TS也能够自动推导出来。

let num = 100; // number
const status = 'success'; // 'success'
const user = {
  name: 'ErMao',
  age: 18
}
// { name: string; age: number }
const arr = [1,2,3]; // number[]
const mixed = [1,'a']; // (number | string)[]
上下文类型推导

TS 根据“使用场景”推导类型。

window.addEventListener('click', (e) => {
  // e → MouseEvent
});
infer

​infer​ 让 TypeScript 通过模式匹配提取类型的一部分。

它只能用于条件类型 extends ? xx : xx​ 中。

type MyReturn<T> = T extends (...args: any[]) => infer R ? R : never;
type First<T extends any[]> = T extends [infer F, ...any[]] ? F : never;

体操:github.com/type-challe…