返回介绍

十四、类型检查机制

发布于 2024-09-07 18:09:17 字数 4389 浏览 0 评论 0 收藏 0

14.1 类型检查机制

编译器在做类型检查时,秉承的一些原则,表现出的一些行为

作用:辅助开发,提高开发效率

  • 类型推断
  • 类型兼容性
  • 类型保护

所谓类型推断:不需要指定变量的类型(函数的返回值类型),TS 可以根据某些规则自动的为其推断出一个类型

  • 基础类型推断
  • 最佳通用类型推断
  • 上下文类型推断

基础类型推断,从右向左。但是有些是从左向右推断

如事件

// ts 根据 onkeydown 推断出类型
window.onkeydown = event=>{
  console.log(event)
}

通过类型断言阻断 TS 的类型推断

interface Foo {
  bar: number
}

//let foo = {} as Foo
//foo.bar = 1

let foo: Foo = {
  bar: 1
}

14.2 类型保护机制

联合类型适合于那些值可以为不同类型的情况。 但当我们想确切地了解是否为 Fish 或者是 Bird 时怎么办? JavaScript 里常用来区分这 2 个可能值的方法是检查成员是否存在。如之前提及的,我们只能访问联合类型中共同拥有的成员

不同的判断方法有不同的使用场景:

  • typeof :判断一个变量的类型
  • instanceof :判断一个实例是否属于某个类
  • in :判断一个属性是否属于某个对象
  • 类型保护函数:某些判断可能不是一条语句能够搞定的,需要更多复杂的逻辑,适合封装到一个函数内
function getLanguage(type: Type) {
  let lang = type === type.Strong ? new Java(): new Javascript()
  
  // 类型保护 instanceof
  if(lang instanceof Java){
    lang.hellJava()
  }else {
    lang.hellJavaScript()
  }
  
  // in
  if('java' in lang) {
    lang.hellJava()
  }else {
    lang.hellJavaScript()
  }
  
  // 类型保护函数方式
  if(isJava(lang)) {
    lang.hellJava()
  }else {
    lang.hellJavaScript()
  }
}

// 创建一种类型保护函数
function isJava(lang: Java | Javascript): lang is Java {
  // 类型断言
  return (lang as Java).lang.helloJava !== undefined
}
let pet = getSmallPet()

// 每一个成员访问都会报错
if (pet.swim) {
  pet.swim()
} else if (pet.fly) {
  pet.fly()
}

为了让这段代码工作,我们要使用类型断言

let pet = getSmallPet()

if ((pet as Fish).swim) {
  (pet as Fish).swim()
} else {
  (pet as Bird).fly()
}

14.2.1 用户自定义的类型保护

  • 这里可以注意到我们不得不多次使用类型断言。如果我们一旦检查过类型,就能在之后的每个分支里清楚地知道 pet 的类型的话就好了。
  • TypeScript 里的类型保护机制让它成为了现实。 类型保护就是一些表达式,它们会在运行时检查以确保在某个作用域里的类型。定义一个类型保护,我们只要简单地定义一个函数,它的返回值是一个类型谓词
function isFish(pet: Fish | Bird): pet is Fish {
  return (pet as Fish).swim !== undefined
}
  • 在这个例子里, pet is Fish 就是类型谓词。谓词为 parameterName is Type 这种形式, parameterName 必须是来自于当前函数签名里的一个参数名。
  • 每当使用一些变量调用 isFish 时,TypeScript 会将变量缩减为那个具体的类型
if (isFish(pet)) {
  pet.swim()
}
else {
  pet.fly()
}

注意 TypeScript 不仅知道在 if 分支里 petFish 类型;它还清楚在 else 分支里,一定不是 Fish 类型而是 Bird 类型

14.2.2 typeof 类型保护

我们可以像下面这样利用类型断言来写

function isNumber (x: any):x is string {
  return typeof x === 'number'
}

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

function padLeft (value: string, padding: string | number) {
  if (isNumber(padding)) {
  return Array(padding + 1).join(' ') + value
  }
  if (isString(padding)) {
  return padding + value
  }
  throw new Error(`Expected string or number, got '${padding}'.`)
}

然而,你必须要定义一个函数来判断类型是否是原始类型,但这并不必要。其实我们不必将 typeof x === 'number' 抽象成一个函数,因为 TypeScript 可以将它识别为一个类型保护。 也就是说我们可以直接在代码里检查类型了

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 并不会阻止你与其它字符串比较,只是 TypeScript 不会把那些表达式识别为类型保护。

14.2.3 instanceof 类型保护

  • 如果你已经阅读了 typeof 类型保护并且对 JavaScript 里的 instanceof 操作符熟悉的话,你可能已经猜到了这节要讲的内容。
  • instanceof 类型保护是通过构造函数来细化类型的一种方式。我们把之前的例子做一个小小的改造:
class Bird {
  fly () {
  console.log('bird fly')
  }

  layEggs () {
  console.log('bird lay eggs')
  }
}

class Fish {
  swim () {
  console.log('fish swim')
  }

  layEggs () {
  console.log('fish lay eggs')
  }
}

function getRandomPet () {
  return Math.random() > 0.5 ? new Bird() : new Fish()
}

let pet = getRandomPet()

if (pet instanceof Bird) {
  pet.fly()
}
if (pet instanceof Fish) {
  pet.swim()
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文