企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
[TOC] >[success] # 泛型第三部分 - 泛型在类和接口中的使用 之前演示的 **泛型** 都是在 **函数** 中,在 **函数** 的 **参数** 和 **返回值** 中使用 **泛型** ,本章将讲解如何在 **类(Class)** 和 **接口(interface)** 上如何使用。 >[success] ## 泛型在类(Class)中使用 我们新建了一个 **Queue(队列)类** ,**Queue(队列)类** 中有 **2** 个方法,一个是 **进入队列 push 方法** ,另一个是 **离开队列 pop 方法** ,队列是 **先进先出** 的数据结构,所以我们使用了 **push** 与 **shift** 方法,下面的代码中, **ts 文件中,并未报错提醒我们 string 类型数据不能使用 toFixed() 方法(注意:ts 是做 静态类型检查的)**,而在 **运行阶段才提醒我们程序报错** **index.ts** ~~~ // Queue(队列:数据先进先出,像子弹夹一样) class Queue { // 私有属性 private data = []; // 进入队列 push(item){ return this.data.push(item) } // 离开队列 pop(){ return this.data.shift() } } // 实例化队列类 const queue = new Queue() // 向队列插入2个值 queue.push(1) queue.push('str') // 取出队列里的值并且调用 toFixed() 方法 console.log(queue.pop().toFixed()) // 1.toFixed() console.log(queue.pop().toFixed()) // 'str'..toFixed() 此时报错 queue.pop(...).toFixed is not a function ~~~ **运行时图片**: ![](https://img.kancloud.cn/c8/a0/c8a0c63a31ddd811eefbed26693f26b2_1127x315.png) 为什么在 **ts** 代码中没有捕捉到这个错误呢?首先 **它允许你向队列中添加任何类型的数据 ,数据被返回时也可以是任何类型** ,可以看到我们向里面添加 **string 类型数据** ,在 **使用时就会出现无法捕捉错误** ,比如上面的例子,我们弹出时的第 **2** 个数据是 **string 类型数据** ,但我们调用了只有 **number 类型数据** 才有的 **toFixed()** 方法,所以在 **ts** 中没有捕捉到这个错误。假定改用法只有 **number 类型** 才会被添加到 **队列** 中如何解决呢? 1. **方法1** 给 **队列方法** 中传入的 **参数** 来 **约束数据类型** ,来达到目的 **index.ts** ~~~ // Queue(队列:数据先进先出,像子弹夹一样) class Queue { // 私有属性 private data = []; // 进入队列 push(item:number){ return this.data.push(item) } // 离开队列 pop(){ return this.data.shift() } } // 实例化队列类 const queue = new Queue() // 向队列插入2个值 queue.push(1) queue.push('str') // 取出队列里的值并且调用 toFixed() 方法 console.log(queue.pop().toFixed()) // 1.toFixed() console.log(queue.pop().toFixed()) // 'str'..toFixed() 此时报错 queue.pop(...).toFixed is not a function ~~~ **编辑器图片**: ![](https://img.kancloud.cn/77/3e/773e2e9ce75e5cbdd37f91d8c9917352_1144x517.png) 这样 **ts** 就会捕捉到错误了,所以说这是一个很快的解决方法,当然快速也意味着痛苦,假如当你想创建一个 **string 队列** 时,你 **不得不修改队列方法中相当多大量的代码** ,我们真正想要的是 **无论是什么类型被推入队列,被推出的类型,都与被推入的类型是一样的** 。 2. **方法2** 这时我们就要使用第二种方法 **泛型** ,根据 **传入的类型来控制,【传入的数据类型】与【返回的数据类型】** **index.ts** ~~~ // Queue(队列:数据先进先出,像子弹夹一样) class Queue<T> { // 私有属性 private data = []; // 进入队列 push(item: T){ return this.data.push(item) } // 离开队列 pop(): T{ return this.data.shift() } } // 实例化队列类 const queue = new Queue<number>() // 向队列插入2个值 queue.push(1) queue.push('str') // 取出队列里的值并且调用 toFixed() 方法 console.log(queue.pop().toFixed()) // 1.toFixed() console.log(queue.pop().toFixed()) // 'str'..toFixed() 此时报错 queue.pop(...).toFixed is not a function ~~~ **编辑器图片**: ![](https://img.kancloud.cn/2e/26/2e265a4cbb4d11d20c99a4d6322798ce_1031x556.png) 在 **class** 类上使用 **泛型** ,只需要在 **类名后面(Queue)** 添加 **<T\>** ,队列的 **参数** 以及返回参数上写上 **T** , 在 **new 实例化类** 的后面加上需要定义的类型即可,上图中就可以完美的抛出错误了。 >[success] ## 泛型在接口(Interface)中使用 **泛型** 也可以用来 来 **约束接口(Interface)** ,举例,下面代码中有个 **IkeyPair** 接口,它有 **2** 个属性, **key 与 value** 属性,此时我们希望 **key 与 value** 这两个 **属性** 的 **类型** ,都是在 **使用时,动态的传入进去** ,代码如下: **index.ts** ~~~ // 定义接口 interface IkeyPair<T, U> { key: T value: U } // 使用时定义接口数据类型 let kp1:IkeyPair<number, string> = { key:1, value: "string" } let kp2:IkeyPair<string, number> = { key:"string", value: 2 } // 像我们之前定义数组时经常会这样定义 let arr: number[] = [1, 2, 3] // 相当于 let arrTwo: Array<number> = [1, 2, 3] ~~~ >[success] ## 总结 这里总结一下 **泛型** 的 **3** 个章节内容: 1. **第1章**:在 **函数使用时** , **函数的类型推断不会流入到函数体内** ,所以 **使用表达式没法明确建立类型的绑定** ,用 **泛型可以让我们打破这个鸿沟** 。 2. **第2章**: 用 **泛型** 灵活的 **约束参数的类型** ,**不需要参数是个特别死板的类型** ,比如说,我们不希望它是个特定 **string 类型** ,不希望它是一个 **number 类型** ,**而我传入的参数必须有某某属性、某某方法** ,否则就会报错。 3. **第3章**: 本章节讲的内容, 它是创建一个 **拥有特定类型的容器** ,比如 **本章的 interface 与 泛型** , **仿佛给容器贴标签一样** ,比如上面的 **arrTwo** 的 **Array 数组** 贴了一个 **number** 标签,告诉 **arrTwo** ,我要你是一个装 **number** 类型的数组,或者 **在调用类时告诉 Queue\<number> 类,我是一个装着数字的队列** ,你可以 **把它当做成一个可变的参数,在用的时候传入,生成不容类型的容器** 。