Skip to content

第07章 泛型类+泛型接口+泛型类约束的复杂应用【无比重要的深度技能】

3-1 TS 泛型到底有多重要?【 重要程度超乎想象】

  1. Vue3 源码充斥着大量的 TS 泛型。懂 TS 泛型是读懂 Vue3 源码不可逾越的环节。
  2. 前端各种技术的声明文件【 d.ts 文件】 TS 泛型更是随处可见【例如:小到一个 Array,ES6 的 Set,Map, 稍微复杂点的例如:Vue3应用的声明文件,Vuex 底层的声明文件,React 组件声明文件,axios 声明文件,这样的例子举不胜举。】
  3. 现在采用 TS 整合前端框架的大中项目越来越多,而 TS 泛型必然成了你必须攻克的核心技能。如果你近几年在公司做过稍微大点的项目,你的感触会特别深刻。
  4. 后端 Nodejs 和 TS 整合的频次也越来越高,优秀的 Nestjs 框架 就完全采用 TS 开发。
  5. TS 语法是晋级高级前端工程师,拿更高薪水,面试加分不可逾越的学习环节,而泛型语法更是重重之重, 一句我能熟练解说 Vue3 源码中的 TypeScript 语法 会为你的面试加分许多。

3-2 +3-3 【 TS 泛型类】TypeScript 泛型重构简易版的 Java 的 ArrayList 【透彻理解为什么一定要用泛型类】

为什么要用泛型类【 好处】

**好处1:**编译期对类上调用方法或属性时的泛型类型进行安全检查(类型安全检查),不符合泛型实际参数类型(泛型实参类型) 就编译通不过,防止不符合条件的数据增加进来。

**好处2:**一种泛型类型被具体化成某种数据类型后,该数据类型的变量获取属性和方法时会有自动提示,这无疑提高代码开发效率和减少出错率。

3-4 [ TS 泛型类] 泛型类定义+格式 【究竟该怎样给泛型下定义才精准无偏差?】

泛型一种参数化数据类型,具有以下特点的数据类型叫泛型 :

特点一:定义时不明确,使用时必须明确成某种具体数据类型的数据类型。【泛型的宽泛】 这也是为啥叫做参数化的原因-和参数一样,定义时不明确,使用时必须明确。不一样的是一个是数据类型,一个是变量参数。

特点二:编译期间进行数据类型安全检查的数据类型。【泛型的严谨】

特别注意:

  1. 类型安全检查发生在编译期间
  2. 泛型是参数化的数据类型, 使用时明确化后的数据类型就是参数的值

泛型类的格式: class 类名<泛型形参类型> 泛型形参类型一般有两种表示:

  1. A-Z 任何一个字母
  2. 语义化的单词来表示,绝大多数情况,泛型都是采用第一种形式表示,如下:
text
    class ArrayList<T>{  
         array: Array<T>
         add(data:T){
             ....
         }
		 ....
    }

泛型的默认值:泛型形参类型默认值是 any 类型,如果不指定泛型形参类型,那么默认是 any 类型。

text
		class ArrayList<T = any>{  
				 array: Array<T>
				 add(data:T){
						 ....
				 }
		 ....
		}

常常将泛型默认值设置为{}:泛型形参类型默认值是{}类型,如果不指定泛型形参类型,那么默认是{}类型。

text
		class ArrayList<T = {}>{  
				 array: Array<T>
				 add(data:T){
						 ....
				 }
		 ....
		}

3-5 【 TS 泛型类】详细讲解+透彻总结 object 为什么不能替代类上的泛型?

原因一:编译期间 object 无法进行类型安全检查,而泛型在编译期间可以进行类型安全检查

object 接受也只能接受所有的 object 类型的变量,比如有 Customer、Student、Dog 类的实例都是对象类型, 或者自己定义的对象,都可以传递给 object 类型的方法参数或属性, 但如果我们只希望添加Customer类的对象,当添加其他类的对象必须出现编译错误,但是 object 无法做到,就只能用泛型了。

TS中的Object与object的区别:Object是所有对象的父类,object是所有对象的类型。Object是JS中的全局对象,object是TS中的全局对象。

原因二: object 类型数据无法接受非 object 类型的变量,只能接受 object 类型的变量,泛型能轻松做到

正因为 object 接受也只能接受所有的 object 类型的变量,那么如果有一个集合类[数组封装类]有一个 add 方法,允许每次添加指定类型的变量到 add 方法的参数,比如:我们第一轮的希望添加 10 次字符串类型的变量,第二轮的希望添加 10 次整数类型变量,第三轮的希望添加 10 次顾客类型的变量,泛型轻松做到。object 类型数据无法接受任意非 object 类型的变量,object 只能接受所有的 object 类型的变量。

原因三: object 类型数据获取属性和方法时无自动提示,泛型有自动提示

一种泛型类型被具体化成某种数据类型后,该数据类型的变量获取属性和方法时会有自动提示,提高代码开发效率和减少出错率,但在 object 类型的变量无法获取数据类型的属性和方法,降低了体验感和开发效率。

3-6 【 TS 泛型类】详细讲解+透彻总结 any 为什么不能替代类上的泛型?

原因一:编译期间 any 无法进行类型安全检查,而泛型在编译期间可以进行类型安全检查 我们学过: any 是所有类型的父类,也是所有类型的子类如果我们现在是一个宠物店类,希望只能添加 Dog 类,当调用 add 方法添加 Customer、Student 类必定出现编译错误,从而保证了类型安全检查,但是 any 类型无法保证类型安全检查,可以为任意类型,包括 string,number,boolean,null,undefined,never,void,unknown 基础数据类型和数组,类,接口类型, type 类型的变量全部能接受,不会进行无法进行类型安全检查。

**原因二:any 类型可以获取任意数据类型的任何属性和任意方法,而不会出现编译错误导致潜在错误风险,而泛型却有效的避免了此类问题发生 **

any 类型可以获取任何属性和任意方法而不会出现编译错误,因为any可以代表任意数据类型来获取任意属性和任意方法, 但是泛型类型被具体化成某种数据类型后,该数据类型的变量调用该数据类型之外的属性和方法时,出现编译错误,这也减少了代码隐藏潜在错误的风险。

原因三: any 类型数据获取属性和方法时无自动提示,泛型有自动提示

彩蛋:any 类型可以代表任意数据类型来获取任何属性和任意方法而不会出现编译错误, 因为any可以代表任意数据类型来获取任意属性和任意方法:【any 的这个特性是一把双刃剑,当我们需要这么使用,它给我们带来方便,但是大多数情况下我们是不需要这么做的】。

3-7 【 TS 泛型类】—美团外卖美食通用分页类的实现【通用类,移植即用】[共4节]

分页类课程安排: 1. 理解Dao 2. 分页类实现 3. 泛型改写分页类 4. T extends object在分页类中的使用

分页类准备:理解Dao:

DAO【数据访问层】理解:

定义:NodeJS 或其他后端语言(例如 JAVA )中的数据访问层【就是很多类的合集,每一个类就是一个 Dao类】 简单点说:DAO 层的每一个类一般是后端数据表中一个实体的增删改查操作【方法】的封装类。

完成了什么: 页面上的某个功能操作需要的数据都来自某个 DAO 类的一个或者多个方法返回的结果。 【先经过 Service,Service 大家先暂时不用管】。

Dao层出现的意义:

(1)封装性+见名思义【可读性好】:当我们执行查询只需多次调用find相关的方法, 执行删除就调用 delete 相关的方法.....,一目了然。

(2)复用性:复用性表现在方法被重复多次调用,通常页面上多个功能有可能需要调用同一个 Dao 类的同一个方法, 我们举一个简单例子和稍微复杂的例子【权限管理,和本门课程无关,只需知道即可】

举例1:注册和登录我们都需要调用 Dao 类的 findUser方法。

举例2: 权限管理功能中为角色定制权限时【用户为角色重新分配新的权限,删除旧的权限】, 和根据角色查询权限这两个功能都需要用到校色查询权限的方法[findRightByRoleId 】。

(3)分工明确,各司其职,利于维护:每一个 Dao 类把混杂在其他位置中的代码分离出来,单独成类, 后期修改维护都非常方便。

DAO层中的类如何命名:

从规范上要求:DAO 层中的每一个类都命名都以 DAO 结尾,常规的操作一般一个Dao方法就能搞定。 例如页面显示所有的美食数据来自 FoodDao 的 findAllFoods方法,而每一个美食的详细数据来自于 FoodDao 的 findOne。

T extends object 【是泛型约束的一种表现】泛型约束简单点说就是把泛型的具体化数据类型范围缩小

理解T extends object : extends 表示具体化的泛型类型只能是 object 类型,某个变量如果能断言成 object 类型【变量 as object】, 那么这个变量的类型就符合 T extends object 。就是说该变量的类型可以是T的具体化类型。

还记得之前说的new底层发生了什么? 任何一个类或者构造函数的底层都是从 new Object ( )而来,这个 new Object ( )对象的类型就是 object 类型。 这就是说任何类的对象或者构造函数的对象都符合T extends object。

分页类使用 T extends object 的原因:分页类中只添加对象数据,所以泛型要被具体化成一个对象类型才符合要求, 例如多个顾客对象,多个美食对象,而拒绝添加 string,number,其他数据类型。

3-8【 TS 泛型类约束】Vue3 源码 深度掌握 T extends object + extends keyof 的综合运用 【 Vue3源码应用】**【**共3节】

课程安排: 1. 理解 keyof 2. T extends object + extends keyof 组合使用带来的好处【 共两节】

1. 理解 keyof

keyof 表示获取一个类或者一个对象类型 或者一个接口类型的所有属性名[ key ]组成的联合类型。 [如果类或者对象类型或者接口上只有一个属性,那么就是一个单一的属性名的类型]

2. T extends object + extends keyof 组合使用带来的好处

3-9 【 TS 泛型接口+ TS 泛型类】 应用多集合类 +泛型接口"嚼烂"接口+泛型接口的好处 【共2节】

**TS 泛型接口+ TS 泛型类课程安排:**1. 为什么要用接口丶泛型接口准备 2 使用接口实现 LinkedList,ArrayList

1. 为什么要用接口丶泛型接口准备:先了解 ArrayList 和 LinkedList

ArrayList和LinkedList,ES6 的 Set 都属于功能相同但是实现方式不同的集合类

ArrayList 应用场景:ArrayList ,Set 底层都是基于数组二次封装的类,所以查询效率很高,但插入,更新,删除数据的效率低 。

LinkedList 链表的应用场景:基于链表结构,插入,删除数据的效率高【尤其是插入数据效率更高】,但查询效率低,另外 LinkedList 提供了 addFirst【添加元素到首位】,addLast 等更多灵活添加数据的方法**,如果addFirst 用 ArrayList 来实现,效率就很低,因为需要把数组每一个元素进行往后移动位置,但 LinkedList实现只需要改变首个节点的prev和新插入节点的 next 即可,效率比 ArrayList 高出许多。

如果某项目需要频繁插入,更新,添加操作,那么就需要使用 LinkedList,比如:新闻项目,股票系统;而对于查询量大,数据变化小的项目就要 ArrayList,比如人口普查系统。

关于如何讲解 LinkedList :泛型类的实现是有一定难度,展现较高编码水平的类,但考虑不让首次学习本课程的同学感觉到容量丶难度过大,**准备把并 LinkedList 泛型类的实现安排到课程升级时讲解,**老师已提前写完,目前大家只需对 LinkedList有个最基础的了解就可以

说个题外话:如果要给LinkedList评出一个难度系数,那么我们就拿 Java 的 ConcurrentHashMap 底层高并发的源码+红黑树左旋,右旋,变色联合的难度系数来说,难度系数高达7颗星,属于有相当难度的技能之一;可以说90%以上的大厂面试官都说不太清楚或者说不太完整。 那么这样下来,链表难度系数就能达到4.5到5颗星之间,也属于比较有难度的技能之一,日后写入简历,可增加简历含金量。

好处1:降低代码管理成本,提供统一属性和方法命名。

为实现了本接口的所有泛型类提供了统一的方法和属性,避免了不同的泛型类对于相同功能的方法和属性各自命名导致加大代码管理和测试的成本的问题。

好处2:可以从整体上快速通读类的共同方法和属性。

可以把接口当成一个书的目录,快速查看类的方法和属性,这对于首次了解项目的开发人员来说是大为有好处的。

好处3:新增相同功能类时,可以快速搭建类的方法。

好处4:和多态结合增加了项目的扩展性和简洁度,对开发大中项目有好处【 3-10会详解,先了解】

多态体现在:

  1. 父类对象变量可以接受任何它的子类对象
  2. 接口类型对象变量可以接受任何它的实现类的对象

一个父类数组对象变量的每一个元素都可以接受任何一个该父类的子类对象

作者:Long Mo
字数统计:3.8k 字
阅读时长:11 分钟

Contributors

Long Mo
文章作者:Long Mo
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Longmo Docs