一、TypeScript类型约束--对象
对象其实和数组类似,不是限制对象本身的类型,而是对对象属性类型进行限制
结构简化:
对对象做类型限制的好处:
大家都学习过一段时间编程了,会发现咱们经常操作的类型就是对象,尤其经常调用接口,获取对象结果,那么,通过对象类型限制就可以让编辑器很方便的提示你当前对象的属性有哪些。
这就是做完限制之后,很方便的得到提示
关于对象函数类型限制的另一个写法:
- 关于对象中的可选属性如何定制
对象中的可选属性定制跟函数参数的定制一样,使用" ?"完成
注意:
如果盗用可选属性去完成需求,必须使用 ?. 操作符,因为可选属性可能不存在,当不存在的时候就会出现致命错误,TS的优势就是会在编程阶段解决掉错误,所以,必须使用 ?. 操作符。
- 关于对象中的自定义属性如何定制
对于对象虽然可以定制可选属性,但也是约束好某个属性可有可无而已。如果说想在对象中定义一个自定义属性应该怎么办?
为了解决这个问题,可以使用对象的"任意属性"
[ propName: string ]: any
- propName就是一个占位符,换成其他的变量名字也可以。
- :string 是当前属性是一个字符串类型
- :any any表示任意类型的意思,因为TS规定:一旦定义了任意属性,那么确定属性和可选属性的类型都必须是任意属性的类型的子集。
二、TypeScript 类型约束 - 接口
在TS中,一般会用接口(interface)对对象类型做专属约束,那么,什么是接口类型呢?
在面向对象语言中,接口(Interfaces)是一个很重要的概念,它是对行为的抽象,而具体如何行动需要由类(classes)去实现(implement)。
TypeScript 中的接口是一个非常灵活的概念,除了可用于对类的一部分行为进行抽象以外,也常用于对「对象的形状(Shape)」进行描述。
- 如何通过接口(interface)约束对象类型
- 接口如何定义
interface 接口名称 {
属性1: 类型; 属性2: 类型;
}
建议:有些编程语言建议接口前面都加上 I 关键词,比如 “IPerson”。
总结:
1. 定义接口虽然也是花括号,但是接口不是对象,所以属性之间不需要通过逗号分隔!
2. 接口大家看起来是不是和之前学习的类型别名差不多,但是他们有区别,等学完接口的基础知识点之后,在总结区别。
- 接口(interface)中的可选属性和自定义属性
、
- 接口(interface)还可以单独用来约束函数的形状
- 接口(interface)的多次声明
接口可以被多次声明,多次声明的属性约束会被合并。
- 接口(interface)的继承
大家跟着我思考一个问题,比如咱们之前定义了一个 IPerson 接口,他具有人类的所有属性约束,现在咱们想定义一个学生对象,那么学生对象中肯定有人类属性,并且还有一些自己独有的属性,当碰到这种情况的时候,咱们就可以利用接口的继承,让学生的接口继承人类的接口,这样就不用在学生接口中重复定义人类信息了。
- 类型别名模拟接口(interface)的继承功能
使用类型别名也是可以模拟接口的继承功能的!
- 类型别名模拟继承的功能扩展
既然能使用 & 符合链接多个约束,那么是否可以使用 | 符号链接多个约束呢?答案是肯定的。使用 | 符合链接多个约束,表示必须要完全符合某一个约束,而另一个约束可以满足或者部分满足也可以!
- 接口(interface)中的只读属性
如果你需要某个对象的属性只在第一次声明时生效,之后不允许修改,那么,可以通过关键词 readonly 实现。
补充:使用类型别名约束对象类型时,也可以定义只读属性
- 接口(interface)与类型别名约束对象的区别
大家可以发现,使用接口和类型别名都可以约束对象,那么他俩有什么区别呢?
- 命名规范
类型别名使用type关键词声明,并且名称唯一,不可重复
接口使用interface关键词声明,可以重复
类型别名中使用逗号分隔属性约束,接口中使用分号(或者不写)分隔约束。
- 使用范围
类型别名除了可以约束对象以外,还可以给他其他类型定义约束
接口只可以约束对象
- 继承
接口支持继承
类型别名虽然可以通过其他方式实现继承效果,但那不是继承
三、TypeScript类型约束--元组
在TS中,数组类型只能约束类型,而元组则是一个可以约束类型和数量的类型!
- 语法 :let 变量: [类型1,类型2] = [值1,值2]
- 使用
元组类型不仅能够约束类型和能约束个数,这在某些数据初始化时十分有用,比如你要一个存储这经纬度的数组,那么,元组就非常合适了。
- 扩展
元组也可以添加“越界元素”,但是必须是约束类型之一:
四、TypeScript类型约束--类型推论
类型推论其实咱们早就有接触,类型推论就是:TypeScript 会在没有明确的指定类型的时候推测出一个类型,这就是类型推论。
类型推论会发生在两个位置:
- 声明变量时
- 函数的返回值
五、TypeScript类型约束--字面量类型
- 什么是字面量类型
字面量类型就是将一个自定义内容当做类型使用。
- 字面量类型的特点
字面量类型要求你对某一个变量做类型约束时,值必须和字面量类型是一致的!
- 字面量类型的使用场景
字面量类型经常和联合类型一起使用,起到约束值的取值范围的作用!
六、TypeScript类型约束--枚举类型
枚举类型其实跟咱们刚才讲过的字面量类型有点相似,都是起到约束变量值范围的作用。
在详细去学枚举型之前,大家先回忆一个内容,就是《webscoket搭建网页聊天室》,当时咱们为了能够更好的处理数据,对每次的交互数据做了分类:
const MESSAGE = { ERROR: 0, GROUPMESSAGE: 1, GROUPLIST: 2 }
枚举类型就十分类似MESSAGE,不仅起到了分类说明的作用,还能起到限制取值范围的作用
- 语法格式
enum types { Mon, Tue, Wed, Thu }
- 枚举类型的特点
咱们通过一个例子来了解一下枚举类型的特点:
通过上边的例子大家可以看出来,枚举类型不仅可以作为类型约束使用,他还可以作为值去使用。并且跟MESSAGE类似,无需关注值是什么,他的属性可以起到提示作用!
大家可以看下一枚举类型的值:console.log(types)
-枚举类型的分类
(1)数字枚举
当值为数字时,此枚举类型可以成为数字枚举。
默认在属性不赋值的情况下,属性的默认值就是数字,并且从0开始递增;
当然你可以手动给数字赋值,并且有以下特点:数字会从最后一个赋值的属性开始递增
(2)字符串枚举
当值为字符串时,此枚举类型就是字符窜枚举。
相比于没有明确意义的递增值的数字枚举,字符串枚举的成员在运行和调试阶段,更具备明确的含义和可读性。
(3)异构枚举
异构枚举就是枚举类型的值有数字有字符串,不过这种枚举类型大家知道就好,异构枚举出现的情况非常的少
(4)常量枚举
常量枚举是枚举类型的一种定义方式,是在定义枚举类型的时候使用 const 关键词声明!
常量枚举跟数字枚举或者字符串枚举使用方式一样,和他们的区别是:常量枚举会在编译阶段被删除
. 想要理解什么是编译阶段删除,咱们就得理解一下枚举类型的原理!接下来,咱们学习一下枚举类型的原理。
-枚举类型的原理
枚举类型其实就是通过原生JavaScript封装好的一段程序,定制一个枚举类型,然后,在终端通过 tsc ./main.ts 解析TS文件为JS文件,查看枚举类型的源码!
编译:
分析以上解析结果不难发现,其实枚举类型就是一个值为属性、属性为值的普通对象!
(5)回忆常量枚举
常量枚举与普通枚举的区别:常量枚举会在编译阶段被删除
编译:
-枚举类型与字面量类型的一些区别
想要知道枚举类型与字面类型的一些区别,那么就要知道字面量类型存在的一些问题。
(1)使用字面量类型定义一些选项时,如果值的内容需要修改,字面量类型需要修改大量内容。
对于此程序来说,如果想要将Gender 修改成 “美女/帅哥”,那么需要修改的地方有很多。
同样的程序,大家思考如果想要将Gender修改成“美女/帅哥”还会想字面量那么麻烦嘛?
(2)从编译结果来看,字面量类型不会进入到编译结果,而枚举类型会进入到编译结果
字面量类型
编译后
枚举类型
编译后
七、TypeScript类型约束--any类型
any类型就相当于是没有类型约束,因为any类型的数据可以是任意数据类型的数据!所以,在项目中,除非迫不得已,否则不推荐使用any类型去定制类型约束!
八、TypeScript类型约束--类型断言
- 什么是类型断言
类型断言就是手动指定一个值的类型!
- 语法
值 as 类型
接下来咱们通过类型断言的用途来学习一下类型断言如何使用!
- 类型断言的用途
1> 将一个联合类型断言为其中一个类型
2> 将一个父类断言为更加具体的子类
3> 将任何一个类型断言为 any
4> 将 any 断言为一个具体的类型
(1)将一个联合类型断言为其中一个类型
在TS中,因为不确定联合类型中的变量到底是哪个联合类型,所以TS只能提供联合类型中的共有属性和方法!
并且,你使用其他属性还会报错
在这种情况下,咱们就可以使用类型断言强行认定 “ani为Fish类型”,然后去访问他的legs属性!
(2)将一个父类断言为更加具体的子类
接下来,让咱们以一个例子来了解一下这种情况。
- 在你的程序中获取一下页面中的a标签,并且你想使用一下a标签的href属性
- 为什么会出现这种情况?
随便在一个有a标签的页面,获取一下a标签,然后 console.dir() 一下a标签!
console.log() 以更清晰的方式显示对象的结构 console.dir() 显示一个对象所有的属性和方法
将console结果拉到最后,大家可以看到现在的类型才是a标签正确的类型!
但是大家把鼠标放到a标签上看一下类型推论给咱们默认赋值的类型是什么:
将控制台console的对象拉到最后,打开prototype,一直找下去,你会发现类型的关系是: HTMLAnchorElement -> HTMLElement -> Element
也就是说Element是HTMLElement的父类,HTMLElement是HTMLAnchorElement的父类。因为Element类型上是没有href属性,所以你的程序报错了。
- 解决问题
1> 在获取a标签的时候就给他指定具体类型
2> 使用类型断言将类型断言成 HTMLAnchorElement 类型
(3)将任何一个类型断言为any
理想情况下,TypeScript 的类型系统运转良好,每个值的类型都具体而精确。 当我们引用一个在此类型上不存在的属性或方法时,就会报错:
首先先要肯定这种现象,这个报错是很有必要的!
但是,有的时候咱们确实需要在某个对象上临时存储一个属性进去,比如:
现在咱们不想让这个错误出现,这时,就可以将任何一个类型转换成any了,因为any类型上访问任何属性都是可以的。
但是需要注意,将一个变量断言为 any 可以说是解决 TypeScript 中类型问题的最后一个手段。但是它极有可能掩盖了真正的类型错误,所以如果不是非常确定,就不要使用 as any。
(4)将any断言为其他类型
在日常的开发中,我们不可避免的需要处理 any 类型的变量,它们可能是由于第三方库未能定义好自己的类型,也有可能是历史遗留的或其他人编写的烂代码,还可能是受到 TypeScript 类型系统的限制而无法精确定义类型的场景。
遇到 any 类型的变量时,我们可以选择无视它,任由它滋生更多的 any。 我们也可以选择改进它,通过类型断言及时的把 any 断言为精确的类型,亡羊补牢,使我们的代码向着高可维护性的目标发展。
- 看下面这个例子 他的返回值根本没有任何提示!
- 改进一下
完美的提示属性和方法
九、TypeScript类型约束--泛型
- 什么是泛型
泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
- 泛型的基本使用
咱们先来看一种情况,比如我定义函数,要求输入什么就输出什么:
在这个例子中有这么几个问题:
1> 因为我不能确定将来传进去什么类型的数据,所以参数的类型只能约束成any;
2> 因为参数的类型不能确定,所以返回值的类型不能确定,只能是any类型;
3> 因为返回值的类型是any,所以没有了类型提示;
接下来咱们就通过泛型解决上个案例的问题:
解释一下这个案例中的知识点:
1> 在函数名与小括号之间的<T>表示定义了一个泛型T,这里泛型的名字可以随意定义,不是非要叫T。
2> val:T 表示使用泛型T
3> (val:T):T 也表示使用泛型T
4> 调用函数时的 <string> 表示在给泛型赋值
总结:在调用函数时,给泛型赋值,那么使用泛型的位置就会以此值为准!
- 简化泛型的使用
在调用带有泛型的函数时,不是非要写<string>,可以省略;TS 会自动推断参数的类型
- 多类型的参数
泛型也是可以定义多个的,主要是应对多类型的情况!
- 泛型的类型约束
当使用泛型时,经常会用到对象类型的泛型变量,那么就会有一个问题:
当是用参数的某个属性做判断的时候,因为TS不知道此属性是否会存在,所以会报错!这是一种特别常见的场景,要解决这个问题可以使用类型约束来完成!
通过类型约束,解决泛型属性提前访问的问题:
- 类型约束配合一个关键词 keyof 使用
比如现在有这么一种需求,定义一个函数,接收一个对象和对象的某一个属性,然后函数会对此属性的值进行加工!
错误很明显,TS不知道prop是否可以用于obj!
接下来通过 keyof 关键词解决这个问题:
让U继承与T中某一项,这样就可以不仅可以解决之前的错误,还可以在使用函数时,让函数具有属性提示功能!
- 泛型接口
泛型接口就是在定义接口时,可以配合泛型,定制更具约束功能的接口;
其实在咱们平常很多时候都在使用泛型接口,
- 比如定义数组类型的第二种方式:
let arr: Array<number> = [1,2,3];
- 比如使用接口约束函数的形状