一文学会 TypeScript 的 82% 常用知识点(上)

前端专栏 2019-11-22 19:17:55

一文学会 TypeScript 的 82% 常用知识点(上)

对于前端从业者来说,TypeScript(以下简称 TS)已经不算是新技术。

Vue3 的源码基于 TS 编写, Angular 项目默认支持 TS 等。它出现的频率越来越高,而学习难度并不大,大概一个周末可以熟悉。

投入少,产出大,所以最好还是花点时间学习一下。

本文根据 TS handbook 整理出 TS 关键的知识点,并对某些部分作了扩展,希望能帮助大家学习理解。

前言

在学习 TS 之前,需要理解它的两个特点:

  1. TS 是 JS 的超集
  2. TS 给 JS 带来类型系统

这意味着 TS 的根基是 JS,是在 JS 的 基础上添加了类型系统。

类型声明

类型系统的一个特点是使用类型声明。

一文学会 TypeScript 的 82% 常用知识点(上)

观察上述代码,在 JS 中,使用「等号=」给变量赋值。

经过 TS 的扩展,可以使用「冒号:」给变量赋类型。代码中声明变量 foo 的类型是 string。

类型校验

类型系统的另一个特点是进行类型校验。

在 TS 中,需要校验「等号=」左右的类型是否匹配。

一文学会 TypeScript 的 82% 常用知识点(上)

如上述代码所示,值存在明确的类型,TS 会校验左右两侧的类型是否匹配,若不匹配则提示错误。

在了解这些前置知识后,来看看具体的 TS 知识点。

一、基本类型

继承 JS 的基本类型如:string、number、boolean、undefined、null。扩展了其他基本类型如:any、never、void。

1.1 string

表示类型为字符串

一文学会 TypeScript 的 82% 常用知识点(上)

1.2 number

表示类型为数字

一文学会 TypeScript 的 82% 常用知识点(上)

1.3 boolean

表示类型为布尔类型

一文学会 TypeScript 的 82% 常用知识点(上)

1.4 undefined

表示类型为 undefined

一文学会 TypeScript 的 82% 常用知识点(上)

1.5 null

表示类型为 null

一文学会 TypeScript 的 82% 常用知识点(上)

1.6 void

表示类型为 undefined 或 null。

1.6.1 对于变量

一个声明为 void 类型的变量,只能被赋值为 undefined 或 null。这种应用场景较少。

一文学会 TypeScript 的 82% 常用知识点(上)

1.6.2 对于函数

void 用于表示函数没有返回值,只是执行某些操作。这是 void 的普遍应用场景。

一文学会 TypeScript 的 82% 常用知识点(上)

一个没有显式返回的函数,默认 return undefined。符合 void 类型只能被赋值为 undefined 或 null。

1.7 never

表示永远不存在值。

典型例子:一个只会抛出异常的函数,它的返回值并不存在。

一文学会 TypeScript 的 82% 常用知识点(上)

执行函数则抛出错误,连 undefined 都不会返回。

1.8 any

表示可能为任何类型。

典型例子:不确定变量的类型或类型是动态的。

一文学会 TypeScript 的 82% 常用知识点(上)

二、引用类型

TS 中的引用类型,除了 JS 中的数组类型、对象类型,还扩展了枚举类型,元祖类型。

2.1 数组类型

表示由某(些)类型组成的数组。有两种写法:方括号表示法和尖括号表示法。

2.1.1 方括号表示法

一文学会 TypeScript 的 82% 常用知识点(上)

表示 arr 的类型是由数字组成的数组。

2.1.2 尖括号表示法

使用 Array<元素类型>,这是数组泛型的使用形式,关于泛型后续会展开。

一文学会 TypeScript 的 82% 常用知识点(上)

2.2 只读数组

使用 ReadonlyArray<属性类型> 定义只读数组。只读数组只能在数组初始化时定义其值,创建后不能进行修改。

一文学会 TypeScript 的 82% 常用知识点(上)

只读数组和常规数组类型 Array<T> 类似。区别在于:常规数组存在修改数组的方法,只读数组不存在修改数组的方法。

2.3 object

表示类型为对象。除了 string、number、bigint、boolean、 undefined、null、symbol 基本类型外的引用类型。

一文学会 TypeScript 的 82% 常用知识点(上)

2.4 元组

由定长数组构成,数组中的元素是某(些)类型。

一文学会 TypeScript 的 82% 常用知识点(上)

2.5 枚举

使用关键字 enum 定义枚举类型。默认枚举值从 0 开始,可以手动赋值。

一文学会 TypeScript 的 82% 常用知识点(上)

三、类型断言

明确告诉 TS 某个值的类型。有两种写法:尖括号写法、as 写法。

尖括号写法

一文学会 TypeScript 的 82% 常用知识点(上)

as 写法

一文学会 TypeScript 的 82% 常用知识点(上)

注意事项

类型断言和类型转换是有明确区别,不能将它理解成类型转换。

在阐述此注意事项之前,先引入另一个概念:联合类型。

一文学会 TypeScript 的 82% 常用知识点(上)

在上述代码中,string | number 表示的就是一个联合类型,意味着 answer 的类型可以为 string 或 number。

下面逐步解释类型断言和类型转换之间的区别。

一文学会 TypeScript 的 82% 常用知识点(上)

上述代码中,string | number 联合类型包含 string 类型,所以这两种类型之间存在联系。使用 as 将联合类型断言成更加具体的 string 类型。

一文学会 TypeScript 的 82% 常用知识点(上)

上述代码中,将 number 类型断言为 string | number 类型。同样是因为两种类型之间存在联系,所以也允许断言。相当于将一个具体的 number 类型断言成更加广泛的联合类型。

一文学会 TypeScript 的 82% 常用知识点(上)

而将 string 类型断言为 number 类型,这是两种不同的类型,没有任何联系,断言会提示错误。

观察上述三个例子,断言发生在两种类型存在联系的情况,它并不是将一种类型转换成另一种类型。

有些脑瓜子灵活的朋友会想到,借助两次断言来进行类似的类型转换。

一文学会 TypeScript 的 82% 常用知识点(上)

这是欺骗了 TS 校验,后果只能自己承担。

四、接口

上面提到使用 object 描述对象类型。但是,对于具有复杂结构的对象、函数。上述的 object 类型力有不逮。

一文学会 TypeScript 的 82% 常用知识点(上)

上述代码中,即使是空对象,也能通过 object 类型校验,而不会校验对象的结构是否能满足后续使用。

此时,就需要使用接口。在 TS 中,接口是描述值的结构。

一文学会 TypeScript 的 82% 常用知识点(上)

上述代码中,就定义了一个接口来描述参数 o,要求它是一个对象,含有 name 属性,且属性值是字符串。

所以,不符合此结构的空对象 {} 就提示错误。

一般来说,使用关键字 interface 来定义接口。

重写上述接口 ——

一文学会 TypeScript 的 82% 常用知识点(上)

4.1 固定属性

在接口中,使用 属性名:属性类型 的结构定义固定属性。

一文学会 TypeScript 的 82% 常用知识点(上)

如上述代码所示,传入的对象需要具有 name 和 age 两个属性,否则会报错。

4.2 可选属性

在接口中,使用 属性名?:属性类型 的结构定义可选属性。顾名思义,可选属性可以存在,也可以不存在。

一文学会 TypeScript 的 82% 常用知识点(上)

4.3 只读属性

在接口中,使用 readonly 属性名:属性类型 的结构定义只读属性。只读属性只能在属性初始化时定义其值,定义后不能进行修改。

一文学会 TypeScript 的 82% 常用知识点(上)

4.4 额外检查

TS 会对对象字面量进行额外的属性检查。

一文学会 TypeScript 的 82% 常用知识点(上)

在上述代码中,接口 Point 定义了两个可选属性,对象字面量中属性 x 和接口兼容,属性 z 是多余无意义的。

虽然实际的属性比接口定义的多,按照常规理解,这是可以通过类型校验,但事实却相反。

TS 对于对象字面量是会进行额外的属性检查,体现在:

当对象字面量赋值给变量或它直接作为参数传递给函数时,如果对象字面量的属性没有在接口中定义,则会报错。

换句话说,对于对象字面量,当它直接赋值给变量和函数参数时,它的属性不能比接口描述的多。

这里重点是直接赋值,如果像例子中,先将对象字面量赋值给变量,再通过变量传参,这样间接的方法可以绕过额外的检查。

4.5 可索引类型

可索引类型包括字符串索引类型与数字索引类型。

4.5.1 字符串索引类型

字符串索引类型具有字符串索引签名。

一文学会 TypeScript 的 82% 常用知识点(上)

4.5.2 数字索引类型

数字索引类型具有数字索引签名

一文学会 TypeScript 的 82% 常用知识点(上)

4.5.3 混合索引签名

属性和索引签名可以形成混合索引签名,但是属性需要和索引签名类型匹配。

一文学会 TypeScript 的 82% 常用知识点(上)

另外,TS 允许同时使用上述两种签名,但是数字索引返回值的类型,它必须是字符串索引返回值类型的子类型。

因为对于 JS 来说,当使用数字索引时,会将它转换成字符串进行索引。所以它们需要保持一致。

一文学会 TypeScript 的 82% 常用知识点(上)

上述代码中,ThreeD 是 TwoD 的子类型,所以接口 PointA 正确。

4.5.4 只读索引签名

可以将索引签名设置为只读,只能在数组初始化时定义其值,创建后不能进行修改。

一文学会 TypeScript 的 82% 常用知识点(上)

4.6 函数类型

函数类型具有调用签名。

一文学会 TypeScript 的 82% 常用知识点(上)

如上述代码所示,调用签名包括参数列表和返回值类型。

对于函数类型来说,它校验的值当然是函数 ——

一文学会 TypeScript 的 82% 常用知识点(上)

如上述代码所示,函数的参数名可以与签名的参数名不同。关键是对应位置的参数类型需要相同。

4.7 类类型

这里的概念有些复杂,如果有良好的 JS 基础,会较易理解。

类的关键字是 Class,由 ES6 开始引入,并逐步完善。本质上 Class 属于语法糖,是基于 prototype 原型链实现的。

一文学会 TypeScript 的 82% 常用知识点(上)

而无论是 ES5 或 ES6,属性 age 是在创建的实例上,而方法 getAge() 是在实例的原型链上。

一文学会 TypeScript 的 82% 常用知识点(上)

而类本身,它是不存在 age 属性 和 getAge() 方法的。

一文学会 TypeScript 的 82% 常用知识点(上)

当然,我们也可以给类本身定义属性和方法。 但给类本身定义的属性和方法并不能通过实例直接访问。

一文学会 TypeScript 的 82% 常用知识点(上)

所以,类与实例的属性和方法是割裂的。

TS 将描述类的属性和方法部分称为类的静态部分类型,将描述实例的属性和方法部分称为类的实例部分类型。

一文学会 TypeScript 的 82% 常用知识点(上)

在上述代码中,使用关键字 implements 描述类实现了接口。更准确的是:描述 类的实例部分 实现了接口。

实现该接口的类的实例,它是具有 age 属性,getAge() 方法。而类本身(即类的静态部分)具有何种属性与方法,上述代码没有进行定义。

所以,这里所说的类类型,实际上是类(实例的)类型。

关于如何定义类的静态部分的类型,在后续会详细介绍。

4.8 接口继承

接口的可以使用关键字 extends 定义继承。一个接口可以继承多个接口。

一文学会 TypeScript 的 82% 常用知识点(上)

4.9 混合类型

一个对象可能混合多种类型。

例如定义一个带版本号的函数——

一文学会 TypeScript 的 82% 常用知识点(上)

值得注意,上述使用类型断言,将函数断言为 Fn,即使 fn 在创建时并不存在 version 属性。

4.10 接口继承类

当接口继承类类型时候,表现在继承类的成员和结构,但不包括其实现。

接口继承类的一个场景是,定义一个子类的类类型。

一文学会 TypeScript 的 82% 常用知识点(上)

结语

由于文章篇幅问题,全文拆分成上下两篇发布。

本篇主要介绍了 TS 的基本类型,引用类型、类型断言、接口等知识点,了解上述的知识点可以阅读部分 TS 代码。

下篇涉及函数、类、泛型等稍微复杂的知识点。

上一篇:TypeScript入门-高级类型


下一篇:nodejs中的fs模块中的方法