关于前端:TypeScript-高级类型你了解几个

38次阅读

共计 5316 个字符,预计需要花费 14 分钟才能阅读完成。

前言

不可否认,现在 TypeScript 已成为一个前端工程师的所须要具备的基本技能。谨严的类型检测,一方面是进步了程序的 可维护性 健壮性 ,另一方面也在耳濡目染地进步咱们的编程思维,即 逻辑性

那么,明天我将会通过联合 理论开发场景 Vue 3.0 源码 中的局部类型定义来简略聊聊 TypeScript 中的高级类型。

interface

interface 被用于对所有具备 构造 的数据进行类型检测。例如,
在理论开发当中,咱们会定义一些对象或数组来形容一些视图构造。

定义对象

常见的有对表格的列的定义:

const columns = [{key: "username", title: "用户名"},
  {key: "age", title: "年龄"},
  {key: "gender", title: "性别"}
]

而这个时候,咱们就能够通过定义一个名为 column 的接口:

interface column {
  key: string,
  title: string
}

// 应用
const columns: column[] = [{key: "username", title: "用户名"},
  {key: "age", title: "年龄"},
  {key: "gender", title: "性别"}
]

定义函数

咱们平时开发中应用的 axios,它的调用形式会有很多种,例如 axios.request()axios.get()axios.put()。它实质上是定义了这么一个接口来束缚 axios 具备这些办法,它看起来会是这样:

export interface Axios {request(config: AxiosRequestConfig): AxiosPromise
  get(url: string, config?: AxiosRequestConfig): AxiosPromise
  delete(url: string, config?: AxiosRequestConfig): AxiosPromise
  head(url: string, config?: AxiosRequestConfig): AxiosPromise
  options(url: string, config?: AxiosRequestConfig): AxiosPromise
  post(url: string, data?: any, config?: AxiosRequestConfig): AxiosPromise
  put(url: string, data?: any, config?: AxiosRequestConfig): AxiosPromise
  patch(url: string, data?: any, config?: AxiosRequestConfig): AxiosPromise
}

继承

可复用性 是咱们平时在写程序时须要常常思考的中央,例如组件的封装、工具函数的提取、函数设计模式的应用等等。而对于接口也同样如此,它能够通过继承来复用一些曾经定义好的 interfaceclass

这里咱们以 Vue 3.0 为例,它在 compiler 阶段,将 AST Element 节点 Node 分为了多种 Node,例如 ForNodeIFNodeIfBranchNode 等等。而这些非凡的 Node 都是继承了 Node

Nodeinteface 接口定义:

export interface Node {
  type: NodeTypes
  loc: SourceLocation
}

IFNodeinterface 接口定义:

export interface IfNode extends Node {
  type: NodeTypes.IF
  branches: IfBranchNode[]
  codegenNode?: IfConditionalExpression
}

能够看到 Node 的接口定义是 十分污浊 的,它形容了 Node 所须要具备最根本的属性:type 节点类型、loc 节点在 template 中的起始地位信息。而 IFNode 则是在 Node 的根底上扩大了 branchescodegenNode 属性,以及重写了 Nodetype 属性为 NodeTypes.IF

这里简略介绍一下 IFNode 的两个属性:

  • branches 示意它对应的 elseelse if 的节点,它可能是一个或多个,所以它是一个数组。
  • codegenNode 则是 Vue 3.0 的 AST Element 的一大特点,它形容了该节点的一些属性,例如 isBlockpatchFlagdynamicProps 等等,这些会和 runtime 的时候靶向更新和靶向更新密切相关。

近段时间,我也在写一篇对于 Vue 3.0 如何实现 runtime + compile 优雅地实现靶向更新和动态晋升的文章,应该会在下周末完工。

小结

对于 interface 的介绍和应用,咱们这里点到即止。当然,它还有很多高级的应用例如联合泛型、定义函数类型等等。有趣味的同学能够自行去理解这方面的实战。

穿插类型和联结类型

穿插类型

穿插类型 故名思意 ,有着穿插之效。咱们能够通过穿插类型来实现 多个类型的合并。例如,在 Vue3 中,compile 阶段除了会进行 baseParse 之外,还会进行 transform,这样最初的 AST 才会进行 generate 生成可执行的代码。所以,compile 阶段它就会对应多个 options,即它也是一个穿插类型:

export type CompilerOptions = ParserOptions & TransformOptions & CodegenOptions

而这个 CompilerOptions 类型别名会用于 baseCompiler 阶段:

export function baseCompile(
  template: string | RootNode,
  options: CompilerOptions = {}): CodegenResult {}

而为什么是多个 options 的穿插,是因为 baseCompiler 只是最根底的 compiler,下面还有更高级的 compiler-domcompiler-ssr 等等。

联结类型

同样地,联结类型其有着 合的成果 。即有时候,你心愿一个变量的类型是 String
或者是 number 的时候,你就能够应用联结类型来束缚这个变量。例如:

let numberOrString: number | string
numberOrString = 1
numberOrString = '1'

并且,在理论的开发中应用联结类型,咱们通常会遇到这样的提醒,例如:

interface student {
  name: string
  age: number
}
interface teacher {
  name: string
  age: number
  class: string
}
let person: student | tearcher = {} as any

person.class = "信息 161"
// property 'person' does not exit on type `student`

这个时候,咱们能够利用 类型断言,通知 TypeScript 咱们晓得 person 是什么:

(person as teacher).class = "信息 161"

小结

对于穿插类型和联结类型,应该是属于咱们平时开发中会应用频繁的局部。并且,从它们的概念上,不难理解,它们实质上就是数学中的 并集 交加,只不过在根本类型上有着不同的体现而已。

类型爱护与辨别类型

类型爱护

在讲联结类型的时候,咱们说了它实质上是 交加,这也导致了咱们不能间接应用交加之外的属性或办法。所以,咱们得通过在不同状况下应用类型断言来告知 TypeScript 它是什么,从而应用交加之外的属性。

然而,频繁地应用类型断言无疑升高了代码的可读性。而针对这一问题,TypeScript 提供了类型爱护的机制来缩小类型断言,它是一个 主谓宾句,例如依然是下面的例子,咱们能够实现这样的类型爱护:

function isTeacher(person: Tearcher | Student): person is Teacher {return (<Tearcher>person).class !== undefined
}

而后通过 isTeacher 这个函数来判断以后类型,再进行相应地属性拜访:

if (isTeacher(person)) {
  // 这里拜访 teacher 独有的属性
  person.class = "信息 162"
} else {person.name = "wjc"}

typeof 与 instanceof

有了类型爱护,这个时候咱们就会遇到这样的问题:如果我的 联结类型中存在多个类型,那么岂不是得定义多个相似 isTeacher 这样的借助类型爱护的函数?侥幸的是,在 TypeScript 应用 typeofinstanceof 能够主动实现类型爱护和辨别。

typeof

在 TypeScript 中对 根底类型 应用 typeof 时,会主动进行类型爱护,例如:

function isNumberOrString(param: string | number) {if (typeof param === 'string') {return param.split('')
    } else {return param++}
}

instanceof

不同于 typeof 只能对根底类型进行类型爱护,instanceof 能够实现对 所有类型 的类型爱护。它是通过构造函数来实现类型爱护。

interface Person {
    name: string
    age: string
  }
  class Teacher implements Person {
    name: string
    age: string
    constructor(name: string, age: string) {
        this.name = name
        this.age = age
    }
    teach() {console.log("i am a teacher.")
    }
  }
  class Student implements Person {
    name: string
    age: string
    constructor(name: string, age: string) {
        this.name = name
        this.age = age
    }
    study() {console.log("i am a student.")
    }
  }
  const person: Student | Teacher = null as any

 if (person instanceof Teacher) {person.teach()
 } else {person.study()
 }

小结

对于 typeofinstanceof 其实在 JavaScript 中也是陈词滥调的知识点,因为传统的类型检测咱们会更偏差应用 Object.String.prototype.toString() 来实现。然而,反而在 TypeScript 中,它们两者反而混的蛟龙得水。

类型别名

类型别名,我想大家脑海中第一工夫想到的就是 Webpack 中的 alias 为门路配置别名。然而,TypeScript 中的类型别名与它只是形同,然而意不同。艰深点讲,就是通过它能够给类型起一个名称:

type age = number
const getAge = () => age

// 等同于
interface Age {getAge():number
}

字面量类型

字面量类型是指咱们能够通过应用类型别名和联结类型来实现 枚举类型,例如:

type method = get | GET | post | POST | PUT | put | DELETE | delete

索引类型与映射类型

对于索引类型和映射类型,这里咱们通过 loadash 中罕用的一个函数 pluck 来解说:

在 JavaScript 中实现:

function pluck(object, names) {return names.map(name => object[name])
}

在 TypeScript 中实现:

function puck<T, K extends keyof T>(object: T, names: K[]): T[K][] {return names.map(name => object[name])
}

这里咱们为 puck 函数定义了两个泛型变量 TK,其中 K 是继承于 T 中所有属性名的类型,所以形参中 names 被束缚为 T 中属性的数组,这个过程被称为 类型索引 。而对于 puck 函数的返回值 T[K][],则代表返回的值是一个数组,并且数组值被束缚为 T 中属性值为 K 的值,这个过程被称为 索引拜访

了解这两种概念可能会有点艰涩,然而对于每一者都离开去了解过程会比拟有逻辑性。

写在最初

尽管,TypeScript 已成为一项前端工程师的必备技能。然而,置信很多小伙伴还是用的 Javascript 比拟多。所以,可能会存在困扰,我该如何进步 TypeScript 编程能力?其实,这个问题很简略,开源的时代,明天咱们很多问题都能够通过浏览一些开源的我的项目源码来解决。这里,我举荐大家能够尝试着去浏览 Vue3.0 的源码,置信通过浏览,你的 TypeScript 编程能力会有质的飞跃。

写作不易,如果你感觉有播种的话,能够帅气三连击!!!

参考资料:

  • 《TypeScirpt 实战指南》
  • TypeScript 官网文档
  • vuejs/vue-next

)

往期文章回顾

  • 从零到一,带你彻底搞懂 vite 中的 HMR 原理(源码剖析)

正文完
 0