跳转到主要内容
模块 5 · 第 1 课(共 2)课程中的9/14~15 min
模块课程(1/2)

类型保护装置

类型缩小是 TypeScript 中最重要的概念之一。然而,通常,像 typeof 或 instanceof 这样的标准类型保护对于复杂的自定义对象来说是不够的。这就是自定义类型防护的用武之地。

标准型防护罩

让我们首先总结一下 TypeScript 如何使用标准 JavaScript 运算符缩小类型范围:

TS
function processInput(val: string | number) {
  if (typeof val === 'string') {
    // Here 'val' is of type string
    console.log(val.toUpperCase());
  } else {
    // Here 'val' is of type number
    console.log(val.toFixed(2));
  }
}

自定义类型防护(is 运算符)

为了定义自定义类型保护,我们创建一个函数,该函数以 parameterName is Type 格式返回 类型谓词,而不是标准布尔返回类型。

该函数必须返回布尔值(true 或 false)。如果它返回 true,TypeScript 将知道传递的参数是指定的类型。

TS
interface Cat {
  name: string;
  meow(): void;
}

interface Dog {
  name: string;
  bark(): void;
}

// This is a custom type guard
function isCat(animal: Cat | Dog): animal is Cat {
  return (animal as Cat).meow !== undefined;
}

function makeNoise(pet: Cat | Dog) {
  if (isCat(pet)) {
    // Here TypeScript knows 'pet' is a Cat
    pet.meow();
  } else {
    // Here TypeScript knows 'pet' is a Dog
    pet.bark();
  }
}

安全和铸造(as 与类型保护)

类型转换(例如,val as Cat)强制编译器信任开发人员,而无需任何真正的运行时验证。如果运行时对象不符合预期,代码将默默失败或抛出异常。

另一方面,自定义类型防护执行强大且动态的运行时检查,安全且准确地通知 TypeScript 编译器实际的变量类型:

TS
function processInput(input: unknown) {
  // unsafe casting: could crash if input does not have the split method
  // const str = input as string;
  // console.log(str.split(' '));

  // safe guard
  if (isString(input)) {
    console.log(input.split(' ')); // 100% Safe!
  }
}

自己尝试一下

练习 1:字符串类型防护

锻炼#ts.m5.l1.e1
尝试:0加载中...

创建一个名为 isString 的简单函数,充当自定义类型保护来验证未知值是否是字符串。

正在加载编辑器...
显示提示

函数的返回类型必须是 val is string,并且应该使用 typeof 来检查它是否等于 'string'。

3 次尝试后可用的解决方案

练习 2:高级用户类型防护

锻炼#ts.m5.l1.e2
尝试:0加载中...

给定 User 和 PremiumUser 类型,编写一个类型保护函数 isPremiumUser(user: User): user is PremiumUser 来检查用户是否具有“高级”角色。

正在加载编辑器...
显示提示

将签名定义为函数 isPremiumUser(user: User): user is PremiumUser 并将 user.role 与 'premium' 进行比较。

3 次尝试后可用的解决方案

练习 3:自定义错误对象验证

锻炼#ts.m5.l1.e3
尝试:0加载中...

实现 isCustomError(err:unknown): err 是 CustomError 函数,用于验证未知值是否是包含“code”键的非空 CustomError 对象。

正在加载编辑器...
显示提示

首先检查 typeof err 是否为“object”且不为 null,然后使用“in”运算符检查“code”属性。

3 次尝试后可用的解决方案

练习 4:管理类型防护

锻炼#ts.m5.l1.e4
尝试:0加载中...

给定两个接口 User(具有属性 role:string)和 Admin(扩展 User 并具有属性 adminToken:string),编写一个名为 isAdmin 的类型保护函数,该函数接受 User 类型的用户并返回类型谓词 user is Admin。警卫必须检查用户的角色是否等于“admin”。

正在加载编辑器...
显示提示

将签名定义为函数 isAdmin(user: User): user is Admin 并检查 user.role === 'admin' 是否。

3 次尝试后可用的解决方案