当前位置: 首页 > news >正文

16.【TypeScript 教程】TypeScript 泛型(Generic)

TypeScript 泛型(Generic)

本节开始介绍 TypeScript 一些进阶知识点,第一个要介绍的泛型是 TypeScript 中非常重要的一个概念,它是一种用以增强函数、类和接口能力的非常可靠的手段。

使用泛型,我们可以轻松地将那些输入重复的代码,构建为可复用的组件,这给予了开发者创造灵活、可重用代码的能力。

1. 解释

泛型在传统的面向对象语言中极为常见,可以使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据。

通俗来讲:泛型是指在定义函数、接口或者类时,未指定其参数类型,只有在运行时传入才能确定。那么此时的参数类型就是一个变量,通常用大写字母 T 来表示,当然你也可以使用其他字符,如:UK等。

语法:在函数名、接口名或者类名添加后缀 <T>

function generic<T>() {}
interface Generic<T> {}
class Generic<T> {}

2. 初识泛型

之所以使用泛型,是因为它帮助我们为不同类型的输入,复用相同的代码。

比如写一个最简单的函数,这个函数会返回任何传入它的值。如果传入的是 number 类型:

function identity(arg: number): number {return arg
}

如果传入的是 string 类型:

function identity(arg: string): string {return arg
}

通过泛型,可以把两个函数统一起来:

function identity<T>(arg: T): T {return arg
}

需要注意的是,泛型函数的返回值类型是根据你的业务需求决定,并非一定要返回泛型类型 T:

function identity<T>(arg: T): string {return String(arg)
}

代码解释: 入参的类型是未知的,但是通过 String 转换,返回字符串类型。

3. 多个类型参数

泛型函数可以定义多个类型参数:

function extend<T, U>(first: T, second: U): T & U {for(const key in second) {(first as T & U)[key] = second[key] as any}return first as T & U
}

代码解释: 这个函数用来合并两个对象,具体实现暂且不去管它,这里只需要关注泛型多个类型参数的使用方式,其语法为通过逗号分隔 <T, U, K>

4. 泛型参数默认类型

函数参数可以定义默认值,泛型参数同样可以定义默认类型:

实例演示

function min<T = number>(arr:T[]): T{let min = arr[0]arr.forEach((value)=>{if(value < min) {min = value}})return min
}
console.log(min([20, 6, 8n])) // 6

解释: 同样的不用去关注这个最小数函数的具体实现,要知道默认参数语法为 <T = 默认类型>

5. 泛型类型与泛型接口

先来回顾下之前章节介绍的函数类型:

const add: (x: number, y: number) => string = function(x: number, y: number): string {return (x + y).toString()
}

等号左侧的 (x: number, y: number) => string 为函数类型。

再看下泛型类型:

function identity<T>(arg: T): T {return arg
}let myIdentity: <T>(arg: T) => T = identity

同样的等号左侧的 <T>(arg: T) => T 即为泛型类型,它还有另一种带有调用签名的对象字面量书写方式:{ <T>(arg: T): T }:

function identity<T>(arg: T): T {return arg
}let myIdentity: { <T>(arg: T): T } = identity

这就引导我们去写第一个泛型接口了。把上面例子里的对象字面量拿出来作为一个接口:

interface GenericIdentityFn {<T>(arg: T): T
}function identity<T>(arg: T): T {return arg
}let myIdentity: GenericIdentityFn = identity

进一步,把泛型参数当作整个接口的一个参数,我们可以把泛型参数提前到接口名上。这样我们就能清楚的知道使用的具体是哪个泛型类型:

interface GenericIdentityFn<T> {(arg: T): T
}function identity<T>(arg: T): T {return arg
}let myIdentity: GenericIdentityFn<number> = identity

注意,在使用泛型接口时,需要传入一个类型参数来指定泛型类型。示例中传入了 number 类型,这就锁定了之后代码里使用的类型。

6. 泛型类

始终要记得,使用泛型是因为可以复用不同类型的代码。下面用一个最小堆算法举例说明泛型类的使用:

class MinClass {public list: number[] = []add(num: number) {this.list.push(num)}min(): number {let minNum = this.list[0]for (let i = 0; i < this.list.length; i++) {if (minNum > this.list[i]) {minNum = this.list[i]}}return minNum}
}

代码解释: 示例中我们实现了一个查找 number 类型的最小堆类,但我们的最小堆还需要支持字符串类型,此时就需要泛型的帮助了:

实例演示

// 类名后加上 <T>
class MinClass<T> {public list: T[] = []add(num: T) {this.list.push(num)}min(): T {let minNum = this.list[0]for (let i = 0; i < this.list.length; i++) {if (minNum > this.list[i]) {minNum = this.list[i]}}return minNum}
}let m = new MinClass<string>()
m.add('hello')
m.add('world')
m.add('generic')
console.log(m.min()) // generic

代码解释:

第 2 行,在声明 类 MinClass 的后面后加上了 <T>,这样就声明了泛型参数 T,作为一个变量可以是字符串类型,也可以是数字类型。

7. 泛型约束

语法:通过 extends 关键字来实现泛型约束。

如果我们很明确传入的泛型参数是什么类型,或者明确想要操作的某类型的值具有什么属性,那么就需要对泛型进行约束。通过两个例子来说明:

interface User {username: string
}function info<T extends User>(user: T): string {return 'mybj ' + user.username
}

代码解释: 示例中,第 5 行,我们约束了入参 user 必须包含 username 属性,否则在编译阶段就会报错。

下面再看另外一个例子:

type Args = number | stringclass MinClass<T extends Args> {}const m = new MinClass<boolean>() // Error, 必须是 number | string 类型

代码解释:

第 3 行,约束了泛型参数 T 继承自类型 Args,而类型 Args 是一个由 number 和 string 组成的联合类型。

第 5 行,泛型参数只能是 number 和 string 中的一种,传入 boolean 类型是错误的。

8. 多重类型泛型约束

通过 <T extends Interface1 & Interface2> 这种语法来实现多重类型的泛型约束:

interface Sentence {title: string,content: string
}interface Music {url: string
}class Classic<T extends Sentence & Music> {private prop: Tconstructor(arg: T) {this.prop = arg}info() {return {url: this.prop.url,title: this.prop.title,content: this.prop.content}}
}

代码解释:

第 10 行,约束了泛型参数 T 需继承自交叉类型(后续有单节介绍) Sentence & Music,这样就能访问两个接口类型的参数。

9. 小结

泛型在 TypeScript 中用途广泛,可以灵活的控制类型之间的约束,提高代码复用性,增强代码可读性。

相关文章:

  • SAP PI之Rest adapter
  • GoZero微服务个人探索之路(一)Etcd:context deadline exceeded原因探究及解决
  • Matlab数学建模算法之模拟退火算法(SA)详解
  • openssl3.2 - xx_fetch函数参数名称字符串有效值列表
  • 75、avx2 什么是计算向量化
  • 部署ATS(Apache Traffic Server)和Nginx正向代理服务性能对比
  • go语言初探(一)
  • Oracle数据库避坑:CASE WHEN ‘ ‘ = ‘ ‘ 空字符串比较,预期的结果与判断逻辑的实现之间存在不匹配
  • 抖店商家对接带货主播建议,远离头部主播保平安,附沟通话术模板
  • Apache ActiveMQ RCE CNVD-2023-69477 CVE-2023-46604
  • 计算机导论08-程序设计
  • 微信小程序 - 视图与逻辑 介绍
  • DML的基本操作
  • 风力发电防雷监测浪涌保护器的应用解决方案
  • LeetCode 每日一题 2024/1/8-2024/1/14
  • [译] React v16.8: 含有Hooks的版本
  • ComponentOne 2017 V2版本正式发布
  • css选择器
  • FineReport中如何实现自动滚屏效果
  • HomeBrew常规使用教程
  • miniui datagrid 的客户端分页解决方案 - CS结合
  • Python连接Oracle
  • swift基础之_对象 实例方法 对象方法。
  • underscore源码剖析之整体架构
  • Vultr 教程目录
  • 创建一种深思熟虑的文化
  • 二维平面内的碰撞检测【一】
  • 聊聊directory traversal attack
  • 日剧·日综资源集合(建议收藏)
  • 手写双向链表LinkedList的几个常用功能
  • 说说动画卡顿的解决方案
  • 思否第一天
  • 腾讯大梁:DevOps最后一棒,有效构建海量运营的持续反馈能力
  • 系统认识JavaScript正则表达式
  • 小程序滚动组件,左边导航栏与右边内容联动效果实现
  • 走向全栈之MongoDB的使用
  • 阿里云服务器购买完整流程
  • 京东物流联手山西图灵打造智能供应链,让阅读更有趣 ...
  • ​人工智能之父图灵诞辰纪念日,一起来看最受读者欢迎的AI技术好书
  • #【QT 5 调试软件后,发布相关:软件生成exe文件 + 文件打包】
  • #Spring-boot高级
  • (04)Hive的相关概念——order by 、sort by、distribute by 、cluster by
  • (1/2)敏捷实践指南 Agile Practice Guide ([美] Project Management institute 著)
  • (8)Linux使用C语言读取proc/stat等cpu使用数据
  • (Redis使用系列) Springboot 使用redis实现接口Api限流 十
  • (二)WCF的Binding模型
  • (原創) 系統分析和系統設計有什麼差別? (OO)
  • (转)eclipse内存溢出设置 -Xms212m -Xmx804m -XX:PermSize=250M -XX:MaxPermSize=356m
  • (转)详解PHP处理密码的几种方式
  • .libPaths()设置包加载目录
  • .Net 8.0 新的变化
  • .NET Core 和 .NET Framework 中的 MEF2
  • .net 受管制代码
  • .NET/ASP.NETMVC 深入剖析 Model元数据、HtmlHelper、自定义模板、模板的装饰者模式(二)...
  • .NET/C# 使窗口永不获得焦点