TypeScript5-泛型
泛型是 TS 中一个重要的概念,它可以创建可复用的组件,同时保持对类型信息的一致性。
泛型提供了一种方式使得类型可以被参数化,这样就可以创建可以适用于各种数据类型的函数或类,而不仅仅限于一个数据类型。
一、泛型
先来看一个需求:
我有一个 compiler 函数,我希望这个函数可以接收一个字符串或一个字符串数组,返回值也是一个字符串或一个字符串数组,有什么办法?
结合多种编程语言,我们很容易想到几种解决方法:
1. 使用函数重载
在传统的面向对象编程语言中,例如 Java 或 C++,函数重载允许我们使用相同的函数名定义多个函数,只要它们的参数列表(参数的数量或类型)不同即可。在编译过程中,编译器会通过函数调用时的参数列表判断调用哪个重载函数。
在 JavaScript 中,如果尝试使用相同的函数名定义多个函数,后定义的函数将覆盖先定义的函数。
在 TS 中可以函数重载,如我们可以这样做:
function compliler(template: string): string;
function compliler(template: string[]): string[];
function compliler(template: any) {return template;
}
console.log(compliler('abc')); // "abc"
console.log(compliler(['abc', 'efg'])); // ["abc", "efg"]
// console.log(compliler(123)); // 报错
在这个例子中,compiler 函数有两个重载签名和一个实现函数。第一个重载签名接受一个字符串类型的参数,返回一个 字符串。第二个重载签名接受一个字符串数组类型的参数,返回一个字符串数组。
实现函数接受任意类型的参数,返回参数本身。这个实现函数必须能兼容所有的重载签名,所有对 compiler 函数的调用都会执行这个实现函数。
需要注意的是,TS 在解析函数重载时,会按照重载签名从上到下的顺序进行匹配,所以在编写重载签名时,应该把最精确的定义放在最前面。
使用这个方法可以解决这个需求,但是这么一个简单的需求,写出的代码看起来很麻烦,让人很不舒服,不够简洁,因此此方案作废。
2. 使用联合类型
使用联合类型也可以解决这个需求。
function compiler(template: string | string[]): string | string[] {return template;
}
我们定义了一个 compiler 函数,有一个字符串类型或字符串数组类型的参数,返回值可以是字符串,也可以是字符串数组。
这个方法和使用函数重载的方法相比,代码简洁了许多。但如果参数类型可以是很多种,比如 template 参数同时也可以是数字类型、布尔类型、数字数组等。虽然可以在参数的类型注解后面继续加上 | number | bool | number[],但这样代码的观感也不是很好。
在这个例子中,传入 compiler 函数的参数类型可以和返回值类型不一致。如果想让返回值的类型和参数的类型一致(如函数重载案例的效果),就可以使用类型变量。
3. 使用类型变量
类型变量是 TS 中的一种特殊的变量,只用于表示类型而不是值。通过使用类型变量,我们可以创建出在多种类型之间都可以共享的函数或组件。
// 类型变量
function compiler <T> (template: T): T {// ...return template;
}
尖括号中的 T 就是类型变量,它可以捕获用户传入的一些类型,允许我们跟踪函数中使用的这些类型的信息。这里我们定义了类型变量 T,就可以在函数定义中使用它。我们定义了参数 template 的类型为 T,返回值类型也为 T,这样就保证了参数类型和返回值类型一致。
在这个案例中,T 可以是任意类型,函数运行时也并不知道 T 是什么类型。、
为了解决这个问题,我们可以在调用函数时指定参数的类型。如下所示:
compiler<string>('123');
这里我们指定了传入的参数类型为字符串,后面传入的 '123' 就是一个字符串。
也可以直接传入 '123',TS 有类型推导机制,在编译时编译器会推导出传入的参数为字符串类型,T 这时也就是字符串类型,返回值的类型也自然就是字符串类型。
// 类型推导
compiler('123');
现在返回去看需求,传入的参数需要是字符串类型或字符串数组类型,我们就可以结合联合类型这样做:
compiler<string | string[]>('123');
这个案例中的 compiler 函数就是一个泛型函数。
二、泛型约束
来看下面的案例:
function compiler <T> (template: T): T {console.log(template.length);// ...return template;
}compiler<string>('123');
编辑器可以通过我们传入的参数 '123' 来推断出 T 为字符串类型,但是编译时仍然会报错:Property 'length' does not exist on type 'T'.,表示类型 T 中不存在 length 这个属性。
我们可以使用继承接口的方法来实现泛型约束,通过接口来指定一个泛型必须具有某些属性或方法。
interface Len {length: number;
}function compiler <T extends Len> (template: T): T {console.log(template.length);// ...return template;
}compiler<string>('123');
这里我们先定义一个接口 Len,接口中有一个属性 length,再让类型变量 T 继承这个接口,编译器知道了类型 T 下有 length 这个变量,即可编译成功。
这里需要注意,使用这种方法,我们传入的这个参数必须要有 length 这个属性,否则会报错。如:
compiler([]); // 0
compiler(false); // 报错
第一行我们传入一个数组,我们都知道,数组是有 length 属性的,因此可以正确运行,输出 0。
第二行我们传入一个 false,为布尔类型,布尔类型没有 length 属性,所以会报错:Argument of type 'boolean' is not assignable to parameter of type 'Len'.,表示布尔类型和接口 Len 不匹配。
如果我们修改 Len 接口,在接口中增加一个 split 属性,类型为 函数:
interface Len {length: number;split: Function;
}
修改 Len 接口后,传入 compiler 的参数必须同时具有 length 属性和 split 函数。因此 compiler([]) 会报错,因为数组不具备 split 方法。
三、总结
使用泛型的优势:
1. 函数和类可以轻松的支持多种类型,增强程序的扩展性。
2. 不必写多条函数重载、联合类型声明,增强代码的可读性。
3. 灵活控制类型之间的约束。
相关文章:
TypeScript5-泛型
泛型是 TS 中一个重要的概念,它可以创建可复用的组件,同时保持对类型信息的一致性。 泛型提供了一种方式使得类型可以被参数化,这样就可以创建可以适用于各种数据类型的函数或类,而不仅仅限于一个数据类型。 一、泛型 先来看一…...
IMX6ULL裸机篇之DDR3的时钟配置
一. MMDC 控制器 对于 I.MX6U 来说,有 DDR 内存控制器,否则的话它怎么连接 DDR 呢?MMDC控制器 就是 I.MX6U 的 DDR内存控制器。 MMDC 外设包含一个内核(MMDC_CORE)和 PHY(MMDC_PHY),内核和 PHY 的功能如下: MMDC 内…...
PBDB Data Service:Specimens and measurements(标本和测量)
Specimens and measurements(标本和测量) 描述摘要1. [Single specimen(单个标本)](https://blog.csdn.net/whitedrogen/article/details/130685099)2. [Add specimen records or update existing records(添加标本记录…...
Zookeeper(一)
简介 设计模式角度 Zookeeper:是一个基于观察者模式设计的分布式服务管理框架,它负责存储和管理大家都关心的数据,然后接受观察者的注册,一旦这些数据的状态发生变化,Zookeeper就将负责通知已经在Zookeeper上注册的那…...
Maven(五):Maven的使用——依赖的测试
Maven(五):Maven的使用——依赖的测试 前言一、实验六:测试依赖的范围1、依赖范围1.1 compile 和 test 对比1.2 compile 和 provided 对比1.3 结论 二、实验七:测试依赖的传递性1、依赖的传递性1.1 概念1.2 传递的原则…...
超级独角兽 Databricks 的崛起之路
在数据扩张以及 AI 兴起的时代,数据存储和分析平台拥有巨大价值和能量。 随着互联网数据的爆炸性增长,数据已经成为企业的新型资源,犹如石油般重要。越来越多的企业希望利用各种结构化和非结构化数据来发挥自己的优势。 然而,他…...
python 3.8 + tensorflow 2.4.0 + cuda11.0 的问题
版本匹配 🔗从源代码构建 | TensorFlow 报错:Could not load dynamic library ‘cupti64_110.dll’; dlerror: cupti64_110.dll not found 是因为我电脑中的 cuda 版本以前是 10,现在是 11.4 ,所以需要安装对应版本的 cudatoolk…...
华为杯”研究生数学建模竞赛2021 年中国研究生数学建模竞赛 E 题: 信号干扰下的超宽带(UWB)精确定位问题-参考思路
一、背景 UWB ( Ultra-Wideband )技术也被称之为“超宽带”,又称之为脉冲无线电技术。这是一 种无需任何载波,通过发送纳秒级脉冲而完成数据传输的短距离范围内无线通信技术,并且信 号传输过程中的功耗仅仅有几十 W 。 UWB 因其独有的特点,使其在军事、物联网等各个领…...
Java 中的访问修饰符有什么区别?
Java 中的访问修饰符用于控制类、类的成员变量和方法的访问权限,主要有以下四种: public:公共访问修饰符,可以被任何类访问。public 修饰的类、成员变量和方法可以在任何地方被访问到。 protected:受保护的访问修饰符…...
Go基础篇:接口
目录 前言✨一、什么是接口?二、空接口 interface{}1、eface的定义2、需要注意的问题 三、非空接口1、iface的定义2、itab的定义3、itab缓存 前言✨ 前段时间忙着春招面试,现在也算告一段落,找到一家比较心仪的公司实习,开始慢慢回…...
边缘计算:数字时代的新战场
随着数字化时代的到来,云计算已经成为了各行各业不可或缺的技术支持。但是,由于云计算涉及到数据的传输和存储,对于网络带宽和延迟的要求也非常高,这使得云计算难以满足一些低延迟、高实时性要求的场景。在这种情况下,…...
PBDB Data Service:Fossil occurrences(化石产出记录)
Fossil occurrences(化石产出记录) 描述摘要1. [Single fossil occurrence(单条化石产出记录)](https://blog.csdn.net/whitedrogen/article/details/130519180)2. [List of fossil occurrences(化石产出记录列表&…...
虾皮Shopee商品详情接口(item_get-根据ID取商品详情)代码封装
item_get-根据ID取商品详情接口 通过代码封装该接口可以拿到商品标题,商品价格,商品促销信息,商品优惠价,商品库存,sku属性,商品图片,desc图片,desc描述,sku图片…...
原生js手动实现一个多级树状菜单效果(高度可过渡变化) + 模拟el-menu组件实现(简单版)
文章目录 学习链接效果图代码要点 简单模拟el-menu实现TestTree.vueMenu.vueSubMenu.vue 学习链接 vue实现折叠展开收缩动画 - 自己的链接 elment-ui/plus不定高度容器收缩折叠动画组件 - 自己的链接 vue的过渡与动画理解 Vue transition 折叠类动画自动获取隐藏层高度以及…...
RK3568平台开发系列讲解(Linux内存篇)Linux内存管理框架
🚀返回专栏总目录 文章目录 一、内核态内存分配二、用户态内存分配三、内存篇章更新哪些内容沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇我们一起将整个内存管理的体系串起来。 对于内存的分配需求,可能来自内核态,也可能来自用户态。 一、内核态内存分配…...
你的编程能力从什么时候开始突飞猛进?
关于编程能力突飞猛进的原因和如何突破自己,以下是我的建议。 在过去的几年中,编程领域发生了很多变化。新的语言和技术不断涌现,使得程序员们需要不断学习和提高。作为一名程序员,编程能力的提高是非常重要的,有助于…...
滨州高企认定条件
认定为高新技术企业必须同时满足以下条件: (一)企业在申请认定时需要注册一年以上。 (二)公司通过自主开发、转让、赠与、并购等方式,获得对其主要产品(服务)在技术上发挥核心支持作用的知识产权所有权。 (三)对企业主要产品(服…...
Azkaban学习——单机版安装与部署
目录 1.解压改名 2.修改装有mysql的虚拟机的my.cnf文件 3.重启装有mysql的虚拟机 4.Datagrip创建azkaban数据库,执行脚本文件 5.修改/opt/soft/azkaban-exec/conf/azkaban.properties文件 6.修改commonprivate.properties 7.传入mysql-connector-java-8.0.29…...
table标签-移动端适配
封装一个组件,该组件需要根据不同设备屏幕宽度自适应调整展示方式。对于 PC 端,以类似 el-table 的形式展示数据,而移动端则以一个类似 item 的形式展示每行数据。 可以先在组件中判断设备类型,如以下示例代码所示: …...
Yolov8改进---注意力机制:DoubleAttention、SKAttention,SENet进阶版本
目录 🏆🏆🏆🏆🏆🏆Yolov8魔术师🏆🏆🏆🏆🏆🏆 1. DoubleAttention 2. SKAttention 3.总结...
【Axure高保真原型】引导弹窗
今天和大家中分享引导弹窗的原型模板,载入页面后,会显示引导弹窗,适用于引导用户使用页面,点击完成后,会显示下一个引导弹窗,直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...
Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
Ubuntu系统下交叉编译openssl
一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机:Ubuntu 20.04.6 LTSHost:ARM32位交叉编译器:arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...
C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
UE5 学习系列(三)创建和移动物体
这篇博客是该系列的第三篇,是在之前两篇博客的基础上展开,主要介绍如何在操作界面中创建和拖动物体,这篇博客跟随的视频链接如下: B 站视频:s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...
C++.OpenGL (10/64)基础光照(Basic Lighting)
基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...
相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...
使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
Ubuntu Cursor升级成v1.0
0. 当前版本低 使用当前 Cursor v0.50时 GitHub Copilot Chat 打不开,快捷键也不好用,当看到 Cursor 升级后,还是蛮高兴的 1. 下载 Cursor 下载地址:https://www.cursor.com/cn/downloads 点击下载 Linux (x64) ,…...
Vue 模板语句的数据来源
🧩 Vue 模板语句的数据来源:全方位解析 Vue 模板(<template> 部分)中的表达式、指令绑定(如 v-bind, v-on)和插值({{ }})都在一个特定的作用域内求值。这个作用域由当前 组件…...
