前言
最近工作一直很忙,复盘周期也有所拉长,不过还是会坚持每周复盘。今天笔者将复盘一下typescript在前端项目中的应用,至于为什么要学习typescript,我想大家也不言自明,目前主流框架vue和react以及相关生态的内部构建大部分都采用了typescript,其原因就在于它的静态类型检查极大的提高了代码的可读性和可维护性,而且定位问题非常方便。下面上一份关于typescript的官方定义,方便大家理解:
TypeScript 是由微软开发的自由和开源的编程语言, 是JavaScript 的一个超集,支持 ECMAScript 6 标准。其设计目标是开发大型应用,它可以编译成纯 JavaScript,编译出来的 JavaScript 可以运行在任何浏览器上。
本文将通过介绍ts的核心知识点以及实际案例来带大家轻松掌握typescript。
概要
任何语言的学习都要有学习和思考体系,前端也不例外,笔者将按照如下图所示结构来进行讲解:
正文
我们目前项目开发用的最多的就是webpack,对于ts,我们也很方便的可以通过ts-loader对其进行编译配置,为了降低大家学习ts的难度,笔者推荐采用vue-cli3或者umi直接搭建ts项目,这样可以更快的上手ts开发。
核心知识点
1. 基础类型
TypeScript支持与JavaScript几乎相同的数据类型,此外还提供了实用的枚举类型方便我们使用。接下来我们简单介绍一下这几种类型的用法.
// 布尔类型
以上是typescript中常用的几种类型, 也是我们必须掌握的基本知识. 这里值得补充的是typescript的类型断言, 也是解决ts警告的利器,比如我们确切的知道某种数据的数据类型,我们可以这么做:
let
2. 接口
TypeScript的核心原则之一是对值所具有的结构进行类型检查。
interface
类型检查器不会去检查属性的顺序,只要相应的属性存在并且类型也是对的就可以。其次我们还可以定义可选属性和只读属性. 可选属性表示了接口里的某些属性不是必需的,所以可以定义也可以不定义.可读属性使得接口中的某些属性只能读取而不能赋值. 具体案例如下:
interface
在实际场景中, 我们往往还会遇到不确定属性名和属性值类型的情况, 这种情况往往发生在第三发SDK接入或者后端响应中, 这个时候我们可以利用索引签名来设置额外的属性和类型, 案例如下:
interface
接口除了描述带有属性的普通对象外,也可以描述函数类型。我们需要给接口定义一个调用签名, 参数列表里的每个参数都需要名字和类型。案例如下:
interface
我们在vue和react开发中,也会经常使用class这种类来编写可复用组件和库, 既然ts可以描述函数类型, 那么是不是也可以用来描述类类型呢? 答案是可以的.但是类接口的定义稍微有点复杂, 我们都知道类是具有两个类型的:静态部分的类型和实例的类型. 当一个类实现了一个接口时,只对其实例部分进行类型检查。 constructor存在于类的静态部分,所以不在检查的范围内。. 这句话相当关键, 我们在定义类接口的时候也要主要这一特点, 案例如下:
interface
掌握了这些关键的接口类型和使用方法, 对于ts的学习基本上可以入门了.
3. 类
关于类接口的话题我们在上文已经介绍了, 这里我们来具体了解一下类. 和js的class一致, typescript的类有公共,私有与受保护的修饰符. 具体含义如下: public private 当成员被标记成 private时,它就不能在声明它的类的外部访问 * protected
具体案例如下:
class
同样我们也可以为类中的某个属性定义readonly修饰符和定义static静态属性, 唯一值得说的是抽象类.
抽象类做为其它派生类的基类使用。 它们一般不会直接被实例化。 不同于接口,抽象类可以包含成员的实现细节。 abstract关键字是用于定义抽象类和在抽象类内部定义抽象方法。
有关抽象类的案例简单介绍如下:
abstract
4. 函数
函数类型在上文已经介绍过了, 这里主要在讲一下可选参数这个概念. JavaScript里每个参数都是可选的,可传可不传。 没传参的时候其值就是undefined。 在TypeScript里我们可以在参数名旁使用 ?实现可选参数的功能。 具体案例如下:
function
注意, 我们的可选参数必须跟在必选参数后面.
5. 泛型
我们可以使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据。 这样用户就可以以自己的数据类型来使用组件。泛型是typescript中比较难懂的知识点, 但是非常重要, 几乎任何第三方组件库里都会用到. 我们先来看个最简单的例子:
function
我们给iSay添加了类型变量T。 T帮助我们捕获用户传入的类型(比如:string),这样我们就可以使用这个类型。之后我们再次使用T当做返回值类型。现在我们可以知道参数类型与返回值类型是相同的了。 这允许我们跟踪函数里使用的类型的信息。
我们还可以把泛型变量T当做类型的一部分使用,而不是整个类型, 这样可以增加我们的使用灵活性, 案例如下:
function
类似于函数类型的定义, 我们也可以定义泛型接口, 并且可以把泛型参数当作整个接口的一个参数, 这样我们就能清楚的知道使用的具体是哪个泛型类型. 案例代码如下:
interface
同样的我们还可以定义泛型类.我们只需要使用(<>)括起泛型类型,跟在类名后面即可. 具体案例如下:
class
我们还可以定义泛型约束来更准确的控制类的类型. 案例如下:
interface
6. 高级类型
typescript的高级类型里我们主要讲解如下核心知识点: 交叉类型 联合类型 多态的 this类型
交叉类型是将多个类型合并为一个类型。我们可以把现有的多种类型叠加到一起成为一种类型,下面有个经典的例子供大家参考:
function
我们通过字符 &
联合类型表示一个值可以是几种类型之一。 我们用竖线(|)分隔每个类型,所以 number | string | boolean表示一个值可以是 number, string,或 boolean。具体例子如下:
let
值得注意的是: 如果一个值是联合类型,我们只能访问此联合类型的所有类型里共有的成员。
还有一种常见的需求是, 我们在实现自己的类后,需要支持类方法的链式调用, 这个时候我们应该返回this, 在typescript中我们就需要了解多态的 this类型. 它表示的是某个包含类或接口的子类型。 这被称做 F-bounded多态性。要想在typescript中支持链式, 我们可以这么写:
class
下面一个我们需要知道的知识点是索引类型查询操作符. 一般用keyof表示。 对于任何类型T, keyof T的结果为T上已知的公共属性名的联合。 比如我们定义一个接口Animal:
interface
keyof Animal是完全可以与 'cat' | 'dog'互相替换的。 不同的是如果我们添加了其它的属性到 Animal,例如 pig: string,那么 keyof Animal会自动变为 'cat' | 'dog' | 'pig'。
7. 命名空间
命名空间主要作用是用来组织代码,以便于在记录它们类型的同时还不用担心与其它对象产生命名冲突。 由于命名空间的用法很简单,这里我们以网上比较流行的D3作为例子, 代码如下:
declare
8. 使用第三方类库
// 安装lodash和对应的类型文件
9. 声明文件
// global.d.ts
当然我们还可以定义更多有用的声明, 这里就不一一举例了.
React + Typescript项目实战
1. 使用umi搭建react+typescript项目
为了帮助大家快速上手typescript开发, 这里我们采用umi来搭建一个支持ts的项目, 不熟悉的朋友可以参考笔者之前学习umi的文章.
2. 定义去全局声明文件
我们在项目src目录下创建一个global.d.ts来作为我们全局的声明文件, 用来处理全局声明和兼容第三方库.
3. 使用ts实现工具类库
/**
以上只是几个简单的案例, 还不够完善, 大家可以根据自己的需求实现相应的封装.
4. 在React组件中使用typescript
笔者将在下一篇文章中继续实现该章节, 让大家对实际的typescript开发有一个具体的认识.