TypeScript 从入门到放弃(一):基础类型、接口、函数和类
TypeScript 从入门到放弃(一):基础类型、接口、函数和类
镇长前言
声明:本文中大量的来自 《TypeScript 文档》。
TypeScript
是 JavaScript
的一个超集,主要提供了类型系统和对 ES6
的支持,它由 Microsoft
开发,代码开源于 GitHub
上。随着 TypeScript
的发展,很多的库都在使用它进行开发和重写,如Vue3.0就是通过TS进行开发的。如果不会 TypeScript
根本就读不懂源码。所以说目前来学习TypeScript
正是时候 ,让我们一起入门 TypeScript
吧!
PS: 为了方便书写下面行文
TypeScript
简写为TS
,JavaScript
简写为JS
。
更新:2020-7-31
类型系统
原始类型
众所周知 JS
分为原始数据类型和对象类型。原始数据类型:boolean, number, string, null, undefined
以及 Symbol
。另外,TS 中还提供了 any
,never
, tuple
,void
,enum
等。
在 TS 中使用了类型注解方式声明变量。如强类型语言中的变量类型一样,用于约束变量类型。
布尔值
1 | let isDone: boolean = false |
数值
使用 number
定义数值。需要注意的地方是ES6中的二进制和十进制表示法,都会被编译成十进制数值。
字符串
TS
字符串与ES6中的字符串相同,同样支持模板字符串和插值。
特殊类型
空值
JS 中没有空值的概念,TS 中用 void
表示没有任何返回值的函数。
1 | function foo (): void { |
空值变量取值只有:undefined 和 null。
Null 和 Undefined
Null
和 Undefined
是基本数据类型,与 void
区别是,undefined
和 null
是所有类型的子类型。也就是说 undefined
可是赋值给任意类型变量。
1 | let num: number = undefined |
任意值(any)及类型推断
在 TS 中任意值类型,就是 JS 中定义的变量,可以接收任意类型的值。
1 | let anyVar: any = 1 |
如果不指定具体类型,直接赋值的话 TS
根据值的类型进行推断。
1 | let str = 'aaa' // 推断为 string 类型 |
联合类型
联合类型表示定义的变量可以取多种类型中的一种。
1 | let strOrNumber: string | number |
联合类型可以根据值进行类型推断,从而访问不同类型的方法。
在 TS 中可以为类型起别名,使用 type 关键字。
1 | type StrOrNumber = string | number |
数组
两种定义数组的方式:
- 在元素类型后加[],表示由此类型元素组成一个数组。
- 使用数组泛型,Array<元素类型>
1 | let list: number[] = [1, 2, 3] |
元组
元素类型是一个特殊的数组,已知元素数量和类型,并且元素类型可以不同。
1 | let ta: [string, number] |
枚举类型
枚举类型是对 JS 的一个补充,使用关键字 enum
。
1 | enum Color { Red, Green, Blue } |
默认情况下,枚举从 0 开始编号,也可以手动设置编号 Red = 1
,后面的会自动加一。另外,可以通过编号查找名字 Color[2]
返回的是字符串。
类型断言
这是一种向编译器确认类型的操作。通常使用 as
关键字。
1 | let someValue: any = 'aaaa' |
类型别名
TS 提供了一种类型注解设置别名的便捷语法,类型别名。
1 | type StrOrNum = string | number |
与接口不同,可以为任意的类型注解提供类型别名。
接口 VS 别名 ?
- 类型注解通常用于为联合类型和交叉类型提供语义化的名称,或者为简单对象提供类型别名。
- 接口通常用于需要说明类型注解层次结构的地方,可以使用 implements 和 extends
复杂类型
接口
在 TS 中,使用接口来定义对象的类型。接口是一种可以描述行为的概念,具体操作由类去实现。
1 | interface Person { |
上面的例子中,接口里的属性都是必须的。有些情况下,有些属性是不需要,那如何做?解决方法是使用 可选属性。只需在属性的后面添加一个 ?
表示此属性是可选属性。
例如:
1 | // 含有一个可选类型的接口。 |
一些对象的属性只在对象创建的时候修改值。可以使用 只读属性 ,属性名前加 readonly
。
1 | interface Point { |
readonly vs const
判断该用readonly还是const的方法是看要把它做为变量使用还是做为一个属性。 做为变量使用的话用 const,若做为属性则使用readonly。
有时候可能希望接口可以允许任意类型的属性,使用可索引类型属性。接口定义方式如下。
1 | interface Person { |
注意:存在任意类型时,其他的类型必须是任意类型的子集。
可索引类型索引值可以是 字符串
或 数字
。
1 | interface numOrStr { |
注意,数字索引类型的返回值是字符串索引类型返回值的子类。
以上只是接口基础使用,后面将会学习使用接口定义函数,以及定义类等。
函数
函数是 JS 程序的基础,在 TS 中同样重要,并且 TS 还为函数添加了额外的功能。下面来看看 TS 中如何定义函数吧。
首先回忆一下,在 JS 中两种函数定义方式:函数声明 和 函数表达式。
1 | // 函数声明 |
在 TS 中对函数定义进行了约束。规定返回值类型,参数数量必须相等。
1 | function sum (x: number, y: number): number { |
TS 还提供了 可选参数,如下:
1 | // c? 为可选参数,默认值为0 |
通过接口和别名的方式定义函数。
1 | // 定义一个函数类型的接口 |
在函数中也可以还规定了 可选参数
、参数默认值
和 剩余参数
。可选参数必须在参数列表的末尾;默认参数不用在参数列表的末尾;剩余参数使用 ...rest
方式获取函数中的剩余参数。
PS: 如果使用参数的默认值,需要传入 undefined 而不是 null。
类
在 ES6 中加入了 class
,TS 中的类除了实现了 ES6 中的类的功能外,还添加了一些新的用法。例如,添加权限修饰符。
下面先复习一下,面向对象几个概念。来自《TypeScript入门》
面向对象的三大特性:封装、继承、多态。
- 封装(Encapsulation):将对数据的操作细节隐藏起来,只暴露对外的接口。外界调用端不需要(也不可能)知道细节,就能通过对外提供的接口来访问该对象,同时也保证了外界无法任意更改对象内部的数据。
- 继承(Inheritance):子类继承父类,子类除了拥有父类的所有特性外,还有一些更具体的特性。
- 多态(Polymorphism):由继承而产生了相关的不同的类,对同一个方法可以有不同的响应。比如 Cat 和 Dog 都继承自 Animal,但是分别实现了自己的 eat 方法。此时针对某一个实例,我们无需了解它是 Cat 还是 Dog,就可以直接调用 eat 方法,程序会自动判断出来应该如何执行 eat。
类定义了数据的抽象特点,包含属性和方法。对象是类的实例,通过 new
生成。
- 存取器(getter & setter):用以改变属性的读取和赋值行为。
- 修饰符(Modifiers):修饰符是一些关键字,用于限定成员或类型的性质。比如 public 表示公有属性或方法。
- 抽象类(Abstract Class):抽象类是供其他类继承的基类,抽象类不允许被实例化。抽象类中的抽象方法必须在子类中被实现。
- 接口(Interfaces):不同类之间公有的属性或方法,可以抽象成一个接口。接口可以被类实现(implements)。一个类只能继承自另一个类,但是可以实现多个接口。
类的定义
如果有其他语言基础,TS 中类的定义很简单。
1 | class TPerson { |
这里声明了一个 TPerson
类,有个 name
属性,一个构造函数和一个方法。在构造函数中对 name
进行赋值。方法中访问类 name
属性。最后一行使用 new 创建了一个 TPerson
实例。
TStudent
是继承自 TPerson
,默认用于父类的属性和方法。同时也可以重写父类的方法。需要注意的一点是在子类的构造函数中必须调用 super()
,它会执行基类的构造函数。 而且,在构造函数里访问 this的属性之前,一定要调用 super()
。 这个是 TS 强制执行的一条重要规则。
如果,不想让子类使用父类的某些属性方法,该如何做呢?就需要权限修饰符登场。
权限修饰符
TS 提供了三种访问修饰符:public
、private
和 protected
。
- public 修饰的属性或方法是公有的,可以在任何地方被访问到,默认所有的属性和方法都是 public 的。
- private 修饰的属性或方法是私有的,不能被实例和子类访问。
- protected 修饰的属性或方法是受保护的,它和 private 类似,区别是它在子类中也是允许被访问的。
另外,可以将属性设置为只读的 readonly
。只读属性必须在声明时或构造函数里被初始化。
1 | class TAnimal { |
最后,类的静态成员使用 static
修饰,这种成员只能使用类名来使用。
抽象类和多态
抽象类使用关键字 abstract
定义,抽象类不允许被实例化。抽象类的抽象方法必须被子类实现。
1 | // 抽象类定义方式 |
类和接口
类实现接口
1 | interface Human { |
接口的继承
在 TS 中接口可以像类一样实现继承,如下:
1 | interface Human { |
PS 一个接口可以同时继承多个接口,就是将多个接口合并成一个接口。实现接口的时候需要把接口所有的属性和方法都实现。
接口除了可以继承接口,还可以继承类。相当于把类的成员和方法都抽象了出来。
1 | class Auto { |
极客时间《TypeScript开发实践》中给出的接口和类关系图。
接口和类都是可以继承的;类可以实现接口;接口也可以继承类的公有、私有和受保护的成员。
小结
本篇学习了 TS 中基础类型、函数、接口和类的基本用法,还有很多知识点没有涉及到后期的学习中会继续补充。