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

TypeScript 关于对【泛型】的定义使用解读

目录

    • 概念导读
    • 泛型函数
    • 多个泛型参数
    • 泛型约束
    • 泛型别名
    • 泛型接口
    • 泛型类
    • 总结:


概念导读

泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。使用泛型 可以复用类型并且让类型更加灵活

在这里插入图片描述
泛型实现类型参数化:

在定义这个函数时, 我不决定这些参数的类型
而是让调用者以参数的形式告知,我这里的函数参数应该是什么类型
把类型作为参数,放在尖括号中

泛型的基本使用

案例讲解:

当我们封装一个函数的时候,可能由于业务需求的不同,向函数内,传递的参数类型也不相同。而TS 的语法规范是,在函数定义的时候就需要为每个待接收的形参以及函数的返回值指定类型,如果在我们调用执行函数之前,就将类型给指定死了的话,这就大大降低了,函数的复用性了。


这时候,就可以使用 泛型,来实现这样的需求,将函数所需参数的类型,延后到,当我调用函数的时候,可以根据不同的业务需求再指定其参数类型。这样一来,将大大的提高函数的复用灵活性。


如下案例:

泛型函数

普通函数定义法

function createArray<T>(length: number, value: T): Array<T> {let result: T[] = [];for (let i = 0; i < length; i++) {result[i] = value;}return result;
}createArray(3, 'x'); // ['x', 'x', 'x']

箭头函数定义法

let append = <T>(val: T, num: number): Array<T> => {const arr: T[] = []for (let index = 0; index < num; index++) {arr.push(val)}return arr;
}
console.log(append<string>("一段字符串", 9));
//['一段字符串', '一段字符串', '一段字符串', '一段字符串', '一段字符串', '一段字符串', '一段字符串', '一段字符串', '一段字符串']
console.log(append<number>(10086, 9));
//[10086, 10086, 10086, 10086, 10086, 10086, 10086, 10086, 10086]

我们在函数名后添加了 < T >,其中 T 用来指代任意输入的类型(泛型),后面的参数类型,以及函数返回值的类型,都可以 使用 T 泛型来定义站位。
console.log(append< string >("一段字符串", 9)); 在函数调用的时候再为其指定泛型所具体代表的类型,也可以不手动指定,而让类型推论自动推算出来(推荐手动指定,使其代码更加清晰明了)


多个泛型参数

定义泛型的时候,可以一次定义多个类型参数,同样的在调用的时候再为其指定参数类型,多泛型参数多用于元组类型的数据处理

普通函数写法:

function swap<T, U>(tuple: [T, U]): [U, T] {return [tuple[1], tuple[0]];
}console.log(swap<number, string>([7, 'seven']));

箭头函数写法:

let swap = <T, U>(tuple: [T, U]): [U, T] => {return [tuple[1], tuple[0]];
}console.log(swap<string, number>(["字符串", 100]));

泛型约束

由于泛型,表示数据的类型的待定的,由于事先不知道它是哪种类型,所以不能够随意操作一个待定数据类型身上的方法属性,如果传进来的数据类型没有这个属性方法,则就会引起报错。

如下案例:

function loggingIdentity<T>(arg: T): T {console.log(arg.length);return arg;
}// 报错:  index.ts(2,19): error TS2339: Property 'length' does not exist on type 'T'.

上例中,泛型 T 不一定包含属性 length,所以编译的时候报错了。

这时候,我们就可以通过接口对泛型进行约束,只允许这个函数传入那些包含 length 属性的变量。这就是泛型约束:

interface Lengthwise {length: number;
}function loggingIdentity<T extends Lengthwise>(arg: T): T {console.log(arg.length);return arg;
}

上例中我们使用了 extends 约束了泛型 T 必须符合接口 Lengthwise 的形状,也就是必须包含 length 属性。

此时如果调用 loggingIdentity 函数的时候,传入的 arg 数据类型身上 不包含 length 属性,那么在编译阶段就会报错了,这样一来就大大降低了,会发生在函数体内部的错误了

补充

多个类型的参数之间也可以互相约束

function copyFields<T extends U, U>(target: T, source: U): T {for (let id in source) {target[id] = (<T>source)[id];}return target;
}let x = { a: 1, b: 2, c: 3, d: 4 };copyFields(x, { b: 10, d: 20 });

上例中,我们使用了两个类型参数,其中要求 T 继承 U,也相当于,U 约束了 T ,这样就保证了 U 上不会出现 T 中不存在的字段。


泛型别名

在类型别名 type 的后面使用<T>即可声明一个泛型参数,接口里的其他成员都能使用该参数的类型

type len = {length: number
}let fun = <T extends len>(x: T): number => {return x.length
}
console.log(fun<string>("486789413"));//9

泛型接口

前面提到过, interface 是我们在使用 TypeScript 的时候,用来定义接口的关键字。

如:

interface Person {name: string;age: number;
}
const dome: Person = {name: "sunny",age: 18,
};

上面示例的代码中用声明接口限制了一个对象,我们也可以通过泛型对我们的接口进行改造

在这里插入图片描述

这时候我们通过定义泛型的方式,定义 T ,U 两个占位符,使得我们的接口具备了泛型,更加灵活。但是我们发现 泛型接口和泛型函数不一样,像上图中这样使用,有一个地方报错了,提示我们需要传入类型参数,而不能依赖自动的类型推断,来确实对象属性的类型,其实这也是可以理解,因为我们对一个对象的限制,主要就是限制对象的属性类型,如果我们不确定类型,那我们传入的一切类型都是有效的了。就起不到类型的限制,而前面我们使用泛型函数,虽然没有显示声明类型,但是我们达到了对函数入参和出参进行了统一。

interface Person<T, U> {name: T;age: U;
}
const dome: Person<string, number> = {name: "sunny",age: 18,
};

所以定义泛型接口的时候,正确的写法应该是 对其传入了两个确定的类型。T,U的类型也因此确定了。

泛型接口来约束函数:

interface Person<T> {(value: T): Array<T>
}const printFun: Person<string> = <T>(value: T): Array<T> => {let arr: T[] = [];arr.push(value)return arr;
};
console.log(printFun("233")); //['233']

泛型类

与泛型接口类似,泛型也可以用于类的类型定义中:

class information<T, U>{name: Tage: Uconstructor(name: T, age: U) {this.name = name;this.age = age}
}
let c =  new information<string, number>("张三", 48)

泛型参数的默认类型

在 TypeScript 2.3 以后,我们可以为泛型中的类型参数指定默认类型。当使用泛型时没有在代码中直接指定类型参数,从实际值参数中也无法推测出时,这个默认类型就会起作用。

class information<T = string, U = number>{name: Tage: Uconstructor(name: T, age: U) {this.name = name;this.age = age}
}
let c = new information("李四", 25)

总结:

泛型德优势是什么?

增加类型的复用性和灵活性。

泛型实现的基本方法是什么?

  • 找到不确定类型的部分,为其定义泛型参数
  • 传入参数时,为泛型指定具体的类型

泛型约束的作用是什么?

既可以,保留泛型的灵活性,又限制了一定的规范。


注明:在正式开发中,泛型的使用场景非常多…


🚵‍♂️ 博主座右铭:向阳而生,我还在路上!
——————————————————————————————
🚴博主想说:将持续性为社区输出自己的资源,同时也见证自己的进步!
——————————————————————————————
🤼‍♂️ 如果都看到这了,博主希望留下你的足迹!【📂收藏!👍点赞!✍️评论!】
——————————————————————————————

相关文章:

TypeScript 关于对【泛型】的定义使用解读

目录 概念导读泛型函数多个泛型参数泛型约束泛型别名泛型接口泛型类总结&#xff1a; 概念导读 泛型&#xff08;Generics&#xff09;是指在定义函数、接口或类的时候&#xff0c;不预先指定具体的类型&#xff0c;而在使用的时候再指定类型的一种特性。使用泛型 可以复用类型…...

盛元广通食品药品检验检测实验室LIMS系统

随着食品与制药行业法规标准的日益提高和国家两化融合的不断推进&#xff0c;为保障检验工作的客观、公正及科学性&#xff0c;确保制药企业对于生产、实验室、物流、管理的信息化和智能化需求越来越明确&#xff0c;为确保新品可及时得到科学准确的检测检验结果&#xff0c;盛…...

【数据结构】-- 栈和队列

&#x1f407; &#x1f525;博客主页&#xff1a; 云曦 &#x1f4cb;系列专栏&#xff1a;数据结构 &#x1f4a8;吾生也有涯&#xff0c;而知也无涯 &#x1f49b; 感谢大家&#x1f44d;点赞 &#x1f60b;关注&#x1f4dd;评论 文章目录 前言一、栈&#x1f4d9;1.1 栈…...

使用SpringAop切面编程通过Spel表达式实现Controller权限控制

目录 参考一、概念SpEL表达式 二、开发引入包定义注解定义切面定义用户上下文 三、测试新建Service在方法上注解新建Service在类上注解运行 参考 SpringBoot&#xff1a;SpEL让复杂权限控制变得很简单 一、概念 对于在Springboot中&#xff0c;利用自定义注解切面来实现接口…...

Flutter:简单搞一个内容高亮

内容高亮并不陌生&#xff0c;特别是在搜索内容页面&#xff0c;可以说四处可见&#xff0c;就拿掘金这个应用而言&#xff0c;针对某一个关键字&#xff0c;我们搜索之后&#xff0c;与关键字相同的内容&#xff0c;则会高亮展示&#xff0c;如下图所示&#xff1a; 如上的效果…...

2023/08/10

文章目录 一、计算属性传参二、小程序、h5跳转其他平台授权三、封装popup弹窗四、实现保存海报五、下载图片和复制分享链接 一、计算属性传参 计算属性的值往往通过一个回调函数返回&#xff0c;但是这个回调函数是无法传递参数的&#xff0c;要想实现计算属性传参可以通过闭包…...

LeetCode 1289. 下降路径最小和 II:通俗易懂地讲解O(n^2) + O(1)的做法

【LetMeFly】1289.下降路径最小和 II&#xff1a;通俗易懂地讲解O(n^2) O(1)的做法 力扣题目链接&#xff1a;https://leetcode.cn/problems/minimum-falling-path-sum-ii/ 给你一个 n x n 整数矩阵 arr &#xff0c;请你返回 非零偏移下降路径 数字和的最小值。 非零偏移下…...

Coin Change

一、题目 Suppose there are 5 types of coins: 50-cent, 25-cent, 10-cent, 5-cent, and 1-cent. We want to make changes with these coins for a given amount of money. For example, if we have 11 cents, then we can make changes with one 10-cent coin and one 1-c…...

2023 8 -14链表OJ

&#x1f495;人面只今何处去&#xff0c;桃花依旧笑春风&#x1f495; 作者&#xff1a;Mylvzi 文章主要内容&#xff1a;详解链表OJ题 题目一&#xff1a;环形链表&#xff08;判断链表是否带环&#xff09; 题目描述&#xff1a; 画图分析&#xff1a; 代码实现&#x…...

大数据必回之LSM树

LSM树&#xff08;Log-Structured-Merge-Tree&#xff09;并不像B、红黑树一样是一颗严格的树状数据结构&#xff0c;它其实是一种存储结构&#xff0c;像HBase、RocksDB这些NoSQL存储都是采用LSM树。它是一种分层、有序、面向磁盘的数据结构&#xff0c;核心思想是顺序写性能远…...

Vue中的Object.defineProperty详解

Vue中的Object.defineProperty是一个比较重要的方法&#xff0c;它是可以定义对象中属性的一个方法&#xff0c;相比于在对象中直接定义的对象&#xff0c;它更具有灵活性。 直接定义对象中的属性是这样的&#xff1a; let person {name:张三,address:广东,age:12,} 而Object.…...

MySQL高阶知识点(一)一条SQL【更新】语句是如何执行的

一条SQL【更新】语句是如何执行的 首先&#xff0c;可以确定的说&#xff0c;【查询】语句的那一套流程&#xff0c;【更新】语句也是同样会走一遍&#xff0c;与查询流程不一样的是&#xff0c; 更新语句涉及到【事务】&#xff0c;就必须保证事务的四大特性&#xff1a;ACID&…...

threejs实现模型gltf的动画效果

确保加载模型后模型有animations属性。加载完模型后&#xff0c;在模型中定义mixer的变量值。 // 4、加入加载器 const loader new GLTFLoader(); loader.load("./model/gltf/RobotExpressive/RobotExpressive.glb", function (gltf) {// 赋值动画给mixermixer ne…...

Harmony创建项目ohpm报错

Harmony创建FA模型的项目时报如下错&#xff1a; The registry is empty - edit .ohpmrc file or use "ohpm config set registry your_registry" command to set registry.解决方法&#xff1a; File -> Settings -> Build,Execution,Deployment -> Ohpm …...

44 | 酒店预订及取消的数据分析

1.背景介绍 数据集来自Kaggle网站上公开的Hotel booking demand项目 该数据集包含了一家城市酒店和一家度假酒店的预订信息,包括预订时间、入住时间、成人、儿童或婴儿数量、可用停车位数量等信息。 数据集容量约为12万32 本次数据分析主要包含如下内容: 总览数据,完成对…...

物联网和不断发展的ITSM

物联网将改变社会&#xff0c;整个技术行业关于对机器连接都通过嵌入式传感器、软件和收集和交换数据的电子设备每天都在更新中。Gartner 预测&#xff0c;全球将有4亿台互联设备投入使用。 无论企业采用物联网的速度如何&#xff0c;连接设备都将成为新常态&#xff0c;IT服务…...

加了ComponentScan,但是feign接口无法注入的原因

正文 正确的注入 如果发现无法注入&#xff1a;看看启动类Application是否有加入注解&#xff1a;EnableFeignClients(AppConstant.BASE_PACKAGES) 注意&#xff1a;EnableFeignClients和ComponentScan是两个独立的扫描&#xff0c;所以&#xff0c;如果只配置了ComponentSca…...

C#Winform中DataGridView控件显示行号实例

本文演示C#Winform中如何给DataGridView控件显示行号。 首先创建winform项目,添加DataGridView控件,给控件添加两列。 修改CS代码: using System.Windows.Forms;namespace DataGridviewDemo {public partial class Form1 : Form{public Form1(){InitializeComponent();//添…...

Stable Diffusion WebUI安装和使用教程(Windows)

目录 下载Stable Diffusion WebUI运行安装程序&#xff0c;双击webui.bat界面启动插件安装&#xff08;github&#xff09;模型下载(有些需要魔法&#xff09;安装过程遇到的大坑总结参考的博客 整个过程坑巨多&#xff0c;我花了一个晚上的时间才全部搞定,本教程针对有编程基础…...

LeetCode 35题:搜索插入位置

题目 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 示例 1: 输入: nums [1,3,5,6], target 5 输出: 2示例 2:…...

[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解

突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 ​安全措施依赖问题​ GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

Admin.Net中的消息通信SignalR解释

定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)

文章目录 1.什么是Redis&#xff1f;2.为什么要使用redis作为mysql的缓存&#xff1f;3.什么是缓存雪崩、缓存穿透、缓存击穿&#xff1f;3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

Linux云原生安全:零信任架构与机密计算

Linux云原生安全&#xff1a;零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言&#xff1a;云原生安全的范式革命 随着云原生技术的普及&#xff0c;安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测&#xff0c;到2025年&#xff0c;零信任架构将成为超…...

【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制

使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下&#xff0c;限制某个 IP 的访问频率是非常重要的&#xff0c;可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案&#xff0c;使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...

FFmpeg:Windows系统小白安装及其使用

一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】&#xff0c;注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录&#xff08;即exe所在文件夹&#xff09;加入系统变量…...

HTML前端开发:JavaScript 获取元素方法详解

作为前端开发者&#xff0c;高效获取 DOM 元素是必备技能。以下是 JS 中核心的获取元素方法&#xff0c;分为两大系列&#xff1a; 一、getElementBy... 系列 传统方法&#xff0c;直接通过 DOM 接口访问&#xff0c;返回动态集合&#xff08;元素变化会实时更新&#xff09;。…...

土建施工员考试:建筑施工技术重点知识有哪些?

《管理实务》是土建施工员考试中侧重实操应用与管理能力的科目&#xff0c;核心考查施工组织、质量安全、进度成本等现场管理要点。以下是结合考试大纲与高频考点整理的重点内容&#xff0c;附学习方向和应试技巧&#xff1a; 一、施工组织与进度管理 核心目标&#xff1a; 规…...

ThreadLocal 源码

ThreadLocal 源码 此类提供线程局部变量。这些变量不同于它们的普通对应物&#xff0c;因为每个访问一个线程局部变量的线程&#xff08;通过其 get 或 set 方法&#xff09;都有自己独立初始化的变量副本。ThreadLocal 实例通常是类中的私有静态字段&#xff0c;这些类希望将…...

leetcode_69.x的平方根

题目如下 &#xff1a; 看到题 &#xff0c;我们最原始的想法就是暴力解决: for(long long i 0;i<INT_MAX;i){if(i*ix){return i;}else if((i*i>x)&&((i-1)*(i-1)<x)){return i-1;}}我们直接开始遍历&#xff0c;我们是整数的平方根&#xff0c;所以我们分两…...