title: typescript学习(二)categories: TS
tag: TypeScript
date: 2021-10-17 14:42:34
类型缩小
什么是类型缩小呢?
类型缩小的英文是 Type Narrowing;
我们可以通过类似于 typeof padding === “number” 的判断语句,来改变TypeScript的执行路径;
在给定的执行路径中,我们可以缩小比声明时更小的类型,这个过程称之为 缩小;
而我们编写的 typeof padding === “number 可以称之为 类型保护(type guards);
常见的类型保护有如下几种:
typeof
平等缩小(比如===、!==)
instanceof
in
1. typeof
在 TypeScript 中,检查返回的值typeof是一种类型保护:因为 TypeScript 对如何typeof操作不同的值进行编码。

2. 平等缩小
我们可以使用Switch或者相等的一些运算符来表达相等性(比如===, !==, ==, and != ):

3. instanceof
JavaScript 有一个运算符来检查一个值是否是另一个值的“实例”:

4. in
Javascript 有一个运算符,用于确定对象是否具有带名称的属性:in运算符
- 如果指定的属性在指定的对象或其原型链中,则in 运算符返回true;

认识类的使用
我们来定义一个Person类:
使用class关键字来定义一个类;
我们可以声明一些类的属性:在类的内部声明类的属性以及对应的类型
如果类型没有声明,那么它们默认是any的;
我们也可以给属性设置初始化值;
在默认的strictPropertyInitialization模式下面我们的属性是必须初始化的,如果没有初始化,那么编译时就会报错;
- 如果我们在strictPropertyInitialization模式下确实不希望给属性初始化,可以使用 name!: string语法;
类可以有自己的构造函数constructor,当我们通过new关键字创建一个实例时,构造函数会被调用;
构造函数不需要返回任何值,默认返回当前创建出来的实例;
类中可以有自己的函数,定义的函数称之为方法;

类的继承
面向对象的其中一大特性就是继承,继承不仅仅可以减少我们的代码量,也是多态的使用前提。
我们使用extends关键字来实现继承,子类中使用super来访问父类。
我们来看一下Student类继承自Person:
Student类可以有自己的属性和方法,并且会继承Person的属性和方法;
在构造函数中,我们可以通过super来调用父类的构造方法,对父类中的属性进行初始化;

使用super关键字
class Person{name:stringage:numberconstructor(name:string,age:number){this.name=namethis.age=age}eating(){console.log('eating');}}class Student extends Person{sno:numberconstructor(name:string,age:number,sno:number){//调用父类构造器super(name,age)this.sno=sno}studying(){console.log('studying');}}//此时学生也是有name属性的const stu=new Student('donghuan',22,111)console.log(stu.name);
也可以通过super调用
//方法的重写eating(){super.eating()console.log('student eating');}}//此时学生也是有name属性的const stu=new Student('donghuan',22,111)stu.eating()
类的成员修饰符
在TypeScript中,类的属性和方法支持三种修饰符: public、private、protected
public 修饰的是在任何地方可见、公有的属性或方法,默认编写的属性就是public的;
private 修饰的是仅在同一类中可见、私有的属性或方法;
protected 修饰的是仅在类自身及子类中可见、受保护的属性或方法;
public是默认的修饰符,也是可以直接访问的,我们这里来演示一下protected和private。
private的使用

protected的使用

只读属性readonly
如果有一个属性我们不希望外界可以任意的修改,只希望确定值后直接使用,那么可以使用readonly。仅仅属性本身不能修改。但是如果是对象。是可以修改这个对象的属性

getters和setters

类的静态成员
前面我们在类中定义的成员和方法都属于对象级别的, 在开发中, 我们有时候也需要定义类级别的成员和方法。
在TypeScript中通过关键字static来定义:
class Person{static time:string="22:00"static attend(){console.log('attend');}}//不需要new出来对象,直接访问就可以了console.log(Person.time);Person.attend()
抽象类
我们知道,继承是多态使用的前提。
所以在定义很多通用的调用接口时, 我们通常会让调用者传入父类,通过多态来实现更加灵活的调用方式。
但是,父类本身可能并不需要对某些方法进行具体的实现,所以父类中定义的方法,,我们可以定义为抽象方法。
什么是 抽象方法? 在TypeScript中没有具体实现的方法(没有方法体),就是抽象方法。
抽象方法,必须存在于抽象类中;
抽象类是使用abstract声明的类;
抽象类有如下的特点:
抽象类是不能被实例的(也就是不能通过new创建)
抽象方法必须被子类实现,否则该类必须是一个抽象类

类的类型
类本身也是可以作为一个数据类型的

接口的声明
在前面我们通过type可以用来声明一个对象类型:

对象的另外一种声明方式就是通过接口来声明:

可选属性
我们可以定义可选属性。

只读属性
接口中也可以定义只读属性:
- 这样就意味着我们再初始化之后,这个值是不可以被修改的;

索引类型

函数类型
前面我们都是通过interface来定义对象中普通的属性和方法的,实际上它也可以用来定义函数类型

当然,其实还是用类型别名来定义函数好一点

接口继承
接口和类一样是可以进行继承的,也是使用extends关键字:
并且接口是支持多继承的。(类不支持多继承)

接口的实现
接口定义后,也是可以被类实现的:
如果被一个类实现,那么在之后需要传入接口的地方,都可以将这个类传入;
这就是面向接口开发;

interface和type的区别
我们会发现interface和type都可以用来定义对象类型,那么在开发中定义对象类型时,到底选择哪一个呢?
- 如果是定义非对象类型,通常推荐使用type,比如Direction、Alignment、一些Function;
如果是定义对象类型,那么他们是有区别的:
interface 可以重复的对某个接口来定义属性和方法;
而type定义的是别名,别名是不能重复的;
字面量赋值
这是因为TypeScript在字面量直接赋值的过程中,为了进行类型推导会进行严格的类型限制。
但是之后如果我们是将一个 变量标识符 赋值给其他的变量时,会进行freshness擦除操作。

认识泛型
软件工程的主要目的是构建不仅仅明确和一致的API,还要让你的代码具有很强的可重用性:
比如我们可以通过函数来封装一些API,通过传入不同的函数参数,让函数帮助我们完成不同的操作;
但是对于参数的类型是否也可以参数化呢?
什么是类型的参数化?
我们来提一个需求:封装一个函数,传入一个参数,并且返回这个参数;
如果我们是TypeScript的思维方式,要考虑这个参数和返回值的类型需要一致:

上面的代码虽然实现了,但是不适用于其他类型,比如string、boolean、Person等类型:

