TypeScript 学习笔记(五):泛型

news2025/3/15 1:29:09

一、泛型是什么?有什么作用

软件工程中,我们不仅要创建一致的定义良好的API,同时也要考虑可重用性。 组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。
在像C#和Java这样的语言中,可以使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据。 这样用户就可以以自己的数据类型来使用组件。
通俗理解:泛型就是解决类、接口、方法的复用性、以及对不特定数据类型的支持。

泛型是什么呢?它可以说是一种类型占位符,也可以说是类型变量,需要注意的是它一种特殊的变量,只用于表示类型而不是值。我们在定义函数、接口或类的时候,不预先指定具体类型,而是在使用的时候再指定类型,先站住位置再说,保证了输入输出保持一致的问题。

这里举个例子说明为什么要使用泛型。我们写一个函数实现返回传递参数的值,并且打印这个值,参数类型为 string,返回值类型也是string,保证输入输出保持一致。

function res(val:string):string {
    console.log(val)
    return val
}

any来定义变量类型,如下:

function res(val:any):any {
    console.log(val)
    return val
}

注意:any 为任意类型,不能保证输入输出保持一致,比如参数类型是 string,返回的却是 number,所以最好不要用 any。

二、具体用法

1. 常用的泛型变量

T (Type) 表示类型;

K (Key) 表示对象中键的类型;

V (Value) 表示对象中值的类型;

U:表示对象中的键类型;

E (Element) 表示元素类型。

我们可以用一个类型变量来传递参数类型和返回值类型:

function res<T>(val:T):T {
    console.log(val)
    return val
}
res(<string>"zhangsan")

2. 泛型类

泛型类可以支持不特定的数据类型,要求传入的参数和返回的参数必须一致,T表示泛型,具体什么类型是调用这个方法的时候决定的。

  • 案例一:
// 在代码中存在一个问题,它允许你向队列中添加任何类型的数据,当然,当数据被弹出
// 队列时,也可以是任意类型。在上面的示例中,看起来人们可以向队列中添加string 类型的数据
// 但是那么在使用的过程中,就会出现我们无法捕捉到的错误,
class Queue {
  private data: unknown[] = []
  push(item: unknown) {
    return this.data.push(item)
  }
  pop() {
    return this.data.shift()
  }
}

const queue = new Queue()
queue.push(1)
queue.push('str')

class Queue<T> {
  private data: T[] = []
  push(item: T) {
    return this.data.push(item)
  }
  pop() {
    return this.data.shift()
  }
}

const queue = new Queue<number>()
queue.push(1)

queue.pop()

在这里插入图片描述

  • 案例二:
class Memory<S> {
  store: S

  constructor(store: S) {
    this.store = store
  }

  set(store: S) {
    this.store = store
  }

  get() {
    return this.store
  }
}

const numMemory = new Memory<number>(1) // <number> 可缺省

const getNumMemory = numMemory.get() // 类型是 number

numMemory.set(2) // 只能写入 number 类型

const strMemory = new Memory('') // 缺省 <string>

const getStrMemory = strMemory.get() // 类型是 string

strMemory.set('string') // 只能写入 string 类型

3. 泛型接口

interface GetArray<T> {
    (arg: T, times: number): T[],
    array: T[]
}

//泛型和 interface
interface KeyPair<T, U> {
  key: T;
  value: U;
}

let kp1: KeyPair<number, string> = { key: 1, value: "str"}
let kp2: KeyPair<string, number> = { key: "str", value: 123}

interface ReturnItemFn<T> {
    (para: T): T
}
const returnItem: ReturnItemFn<number> = para => para //const returnItem: ReturnItemFn<number> = (para: any) => para


interface  Class{
    <T,U>(name:T,score:U):T
}
let func = function <T,U>(name:T,score:U):T{
    return name + ':'+ score
}
func('zhangsan',3)//编译器自动识别参数类型,作为泛型的类型

4. 泛型类接口

//定义操作数据库的泛型类
class MysqlDb<T>{
    add(info: T): boolean {
        console.log(info);
        return true;
    }
}

//想给User表增加数据,定义一个User类和数据库进行映射
class User {
    username: string | undefined;
    pasword: string | undefined;
}
var user = new User();
user.username = "张三";
user.pasword = "123456";
var md1 = new MysqlDb<User>();
md1.add(user);

//想给ArticleCate增加数据,定义一个ArticleCate类和数据库进行映射
class ArticleCate {
    title: string | undefined;
    desc: string | undefined;
    status: number | undefined;
    constructor(params: {
        title: string | undefined,
        desc: string | undefined,
        status?: number | undefined
    }) {
        this.title = params.title;
        this.desc = params.desc;
        this.status = params.status;
    }
}

var article = new ArticleCate({
    title: "这是标题",
    desc: "这是描述",
    status: 1
});
var md2 = new MysqlDb<ArticleCate>();
md2.add(article);

5. 泛型函数

function test <T> (arg:T):T{
  console.log(arg);
  return arg;
}
test<number>(111);// 返回值是number类型的 111
test<string | boolean>('hahaha')//返回值是string类型的 hahaha
test<string | boolean>(true);//返回值是布尔类型的 true

使用方式类似于函数传参,传什么数据类型,T就表示什么数据类型, 使用表示,T也可以换成任意字符串。

三、泛型约束

1.泛型现在似乎可以是任何类型,但实际开发可能往往不是任意类型,需要给以一个范围,这种就叫’泛型约束’关键字(‘extends’)
泛型是具有当前指定的属性写法上’’
2.注意泛型约束是约束泛型的 在<> 这里写

1. 泛型约束

不知道类型就会报错,所以需要对参数类型进行约束

 function res<T>(val:T):T {
    console.log(val.length)
    return val
}
res(<string>"zhangsan") // 报错 类型“T”上不存在属性“length”
//这种写法也可以的哦
//res<string>("zhangsan")

先用 type 声明一个变量类型,让类型变量 T 继承接口 Class :

interface Class{
    name:string,
    age:number
}
function result<T extends Class>(val:T):T {
  console.log(val.name)
  return val
}
result({name:"zhangsan",age:10})
//如果参数中不写age的话,就会报错
//类型“{ name: string; }”的参数不能赋给类型“Class”的参数。
//类型 "{ name: string; }" 中缺少属性 "age",但类型 "Class" 中需要该属性。
result({name:"zhangsan"})

interface ValueWithLength {
    length: number
}

const getArray = <T extends ValueWithLength>(arg: T, times): T[] => {
    return new Array(times).fill(arg)
}
getArray([1, 2], 3)
getArray('123', 3)
getArray({
    length: 2,
}, 3)
getArray(1, 3) // 报错 数字类型没有length

防止思维定式

function getExcludeProp<T extends {props:string}>(obj:T){
    return obj
}
// getExcludeProp({name:'w'}) // 报错
getExcludeProp({name:'w',props:'w'})

2. 泛型约束结合索引类型的使用

  1. 看下面案例想 获取对象value 输出出来
type Info = {
  name:string
  age:number
}

function getVal(obj:Info, key:any) {
  return obj[key] // 报错
}

在这里插入图片描述

  • 正确写法可以利用keyof 吧传入的对象的属性类型取出生成一个联合类型:
type Info = {
  name:string
  age:number
}

function getVal(obj:Info, key:keyof Info) {
  return obj[key]
}
  • 使用泛型

1.利用’索引类型 keyof T 把传入的对象的属性类型取出生成一个联合类型’,再用’extends 做约束’

// 注意泛型约束是约束泛型的 在<> 这里写
type GetVal = <T extends object, K extends keyof T>(obj: T, key: K) => string
function getVal(obj: any, key: any): GetVal {
  return obj[key]
}

getVal({ name: 'w' }, 'name')

3. 多重约束

interface FirstInterface {
  doSomething(): number
}

interface SecondInterface {
  doSomethingElse(): string
}
// // interface ChildInterface extends FirstInterface, SecondInterface {}
 二者等同


class Demo<T extends FirstInterface & SecondInterface> {
  private genericProperty: T

  useT() {
    this.genericProperty.doSomething() // ok
    this.genericProperty.doSomethingElse() // ok
  }
}
interface ValueWithLength {
    length: number
}

const getArray = <T extends ValueWithLength>(arg: T, times): T[] => {
    return new Array(times).fill(arg)
}
getArray([1, 2], 3)
getArray('123', 3)
getArray({
    length: 2,
}, 3)
getArray(1, 3) // 报错 数字类型没有length

四、常用的泛型工具类型

  1. Partial
  2. Readonly
  3. Pick<Type, Keys>
  4. Record<Keys, Type>

1. Partial

用来构造 (创建) 一个类型, 将 Type 的所有属性设置为可选

  • 案例一:
interface Props{
    id:number,
    name:string
}
 type PartialProps = Partial<Props>

使用后 PartialProps里的所有属性都变成可选 即:

type PartialProps = {
    id?: number | undefined;
    name?: string | undefined;
}

  • 案例二:

partial的作用就是将某个类型中的属性全部变为可选项?

interface Person {
  name:string;
  age:number;
}
function student<T extends Person>(arg: Partial<T>):Partial<T> {
  return arg;
}

2. Readonly

用来构造一个类型, 将 Type 的所有属性都设置为 readonly (只读 )

  • 案例一:
interface Props{
    id:number,
    name:string
}
 type ReadonlyProps = Readonly<Props>
 let props : ReadonlyProps = {id:1,name:'xxx'}
        //  props.name='w3f'  这句话 此时会报错


使用Readonly后,对象里面的属性只可读,不可以改变。此时ReadonlyProps为:

type ReadonlyProps = {
    readonly id: number;
    readonly name: string;
}

  • 案例二:

3. Pick

从 Type 中选择一组属性来构造新类型

  • 案例一:
//两个变量表示:1)表示谁的属性 2)表示选择哪一个属性,此属性只能是前面第一个变量传入的类型变量中的属性
 interface Props2{
    id:number,
    name:string,
    age:number
}
 type PickProps = Pick<Props2,'id'|'age'>  //这表示 构造出来的新类型PickProps 只有id age两个属性类型


表示在旧的类型中选择一部分属性,讲选择的属性来重新构造成一个新的类型。此时PickProps为:

type PickProps = {
    id: number;
    age: number;
}

  • 案例二:
interface Info {
    name:string,
    age:number,
    address:string
}

// 选择只使用 字段 这里选择的是name  和age 字段
const a:Pick<Info,'name'|'age'> = {
    name:'w',
    age:12
}

// 当方法需要返回是一个对象时候
const info5 = {
    name: 'lison',
    age: 18,
    address: 'beijing',
}

// 返回的是一个对象 用pick 来做了指定
function pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {
    const res: any = {}
    keys.map((key) => {
        res[key] = obj[key]
    })
    return res
}
const nameAndAddress = pick(info5, ['name', 'address'])
console.log(nameAndAddress)

4. Record

构造一个对象类型,属性键为 Keys ,属性类型为 Type。

  • 案例一:
  //两个变量表示:1)表示对象有哪些属性 2)表示对象属性的类型
  type RecordObj = Record<'a'|'b'|'c',string>  //此处表示 此类型变量有a b c三个键,并且三个的属性值都是string类型
  let obj :RecordObj = {
    a:'1',
    b:'2',
    c:'3'
  }

type RecordObj = {
    a: string;
    b: string;
    c: string;
}

  • 案例二:
    Record<K extends keyof any, T>的作用是将K中所有的属性转换为T类型:
interface PageInfo {
  title: string
}
type Page = 'home'|'about'|'other';
const x: Record<Page, PageInfo> = {
  home: { title: "xxx" },
  about: { title: "aaa" },
  other: { title: "ccc" },
};

5. Exclude

Exclude<T,U>的作用是将某个类型中属于另一个类型的属性移除掉,示例:

type T0 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
const t:T0 ='b';

6. ReturnType

returnType的作用是用于获取函数T的返回类型,示例:

type T0 = ReturnType<() => string>; // string
type T1 = ReturnType<(s: string) => void>; // void
type T2 = ReturnType<<T>() => T>; // {}
type T3 = ReturnType<<T extends U, U extends number[]>() => T>; // number[]
type T4 = ReturnType<any>; // any
type T5 = ReturnType<never>; // any
type T6 = ReturnType<string>; // Error
type T7 = ReturnType<Function>; // Error

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/763010.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【MySQL异常解决】Operation not allowed when innodb_forced_recovery > 0 的解决办法

Operation not allowed when innodb_forced_recovery > 0 的解决办法 一、背景描述二、解决方案三、拓展 一、背景描述 Spring Boot 项目&#xff0c;能正常启动&#xff0c;就是访问数据库时后台报错&#xff0c;Cause: java.sql.SQLException: Operation not allowed whe…

禁用 OPTIONS 请求

背景&#xff1a; 渗透测试结果为 不安全的HTTP方法 OPTIONS 描述 验证 curl -v -X OPTIONS http://localhost/xcall/token/refresh?_t1689589608解决方案&#xff08;2选1&#xff09;&#xff1a; 1.spring项目加上&#xff1a; import org.apache.tomcat.util.descript…

基于xinetd部署pwn题(百分百搭成并且可以nc靶场地址)

这种搭建方法需要你先装好docker&#xff0c;关于docker命令的安装这里就不过多介绍了&#xff0c;下面讲的是基于你已经安装好了docker&#xff0c;我们再使用xinetd在CTFd上部署pwn题目。 首先讲一下我自己搭建pwn题的一些经历&#xff0c;我最开始参考的是用socat命令来搭&a…

【DC-DC】APS54083 降压恒流驱动器大功率深度调光 舞台 RGB 汽车照明 台灯驱动芯片

产品描述 APS54083 是一款 PWM 工作模式,高效率、外围简单、外置功率 MOS 管&#xff0c;适用于 5-220V 输入高精度降压 LED 恒流驱动芯片。输出最大功率150W最大电流 6A。APS54083 可实现线性调光和 PWM 调光&#xff0c;线性调光脚有效电压范围 0.5-2.5V.PWM 调光频率范围 1…

MySQL第四章、表的增删查改(进阶)

目录 一、数据库约束 1.1约束类型 1.2NULL约束 1.3 UNIQUE&#xff1a;唯一约束 1.4 DEFAULT&#xff1a;默认值约束 1.5 PRIMARY KEY&#xff1a;主键约束 1.6 FOREIGN KEY&#xff1a;外键约束 1.7 CHECK约束&#xff08;了解&#xff09; 二、表的设计 2.1 一对一…

轻量级锁实现2——上锁、释放

瀚高数据库 目录 环境 文档用途 详细信息 环境 系统平台&#xff1a;Linux x86-64 Red Hat Enterprise Linux 7 版本&#xff1a;14,13,12,11 文档用途 从底层理解轻量级锁的实现&#xff0c;从保护共享内存的角度理解轻量级锁的使用场景&#xff0c;包括上锁、等待、释放&…

SpringCloud(二)Eureka简介与依赖导入

一、Eureka Eureka能够自动注册并发现微服务&#xff0c;然后对服务的状态、信息进行集中管理&#xff0c;这样当我们需要获取其他服务的信息时&#xff0c;我们只需要向Eureka进行查询就可以了。 像这样的话&#xff0c;服务之间的强关联性就会被进一步削弱。 二、服务注册与…

图像处理之梯度及边缘检测算子

文章目录 一、sobel 算子二、Scharr算子三、Roberts算子四、拉普拉斯算子 梯度是一个量变化的速度&#xff0c;在数学中通常使用求导、求偏导获取梯度或者某一方向上的梯度。 在数字图像中梯度可以看为像素值分别在x,y方向上的变化速度&#xff0c;因为数字图像的离散型&#x…

vue3 如何将页面生成 pdf 导出

原文链接&#xff1a;vue3 如何将页面生成 pdf 导出 前言 最近工作中有需要将一些前端页面(如报表页面等)导出为pdf的需求&#xff0c;博主采用的是html2Canvas jspdf。 步骤 1.引入两个依赖 npm i html2canvas npm i jspdf点击 jsPDF GitHub、jsPDF 文档 查看关于jsPDF更多…

连续两个季度利润暴跌95%以上,三星回不去了

这两年&#xff0c;小编本人电脑的 CPU、显卡等核心硬件毫无升级欲望不愿折腾。 反倒是内存条、固态硬盘容量不知不觉翻了好几倍! 老实说&#xff0c;对大多数像咱这样的普通用户来说&#xff0c;CPU、显卡从两三年前的主流型号升级到现在的主流型号&#xff1b; 价格明显上涨…

求助:交流耦合放大电路(HPF)的阻抗匹配问题

1、同向的交流耦合放大电路 电路如下图所示&#xff0c;信号源是一个上升时间1ns&#xff0c;下降时间15ns的脉冲信号&#xff0c;经过传输线的时延为5ns&#xff0c;然后通过放大器的同向交流耦合放大&#xff0c;这里我们可以明确的直到&#xff0c;下图中的R25就是端接电阻…

JavaWeb——基于Spring Boot的图书数字化管理系统的设计与实现

课程设计总结 1 概述 1.1 项目开发背景 随着信息技术的快速发展&#xff0c;数字化管理已经成为各行各业提高效率和管理水平的重要手段。在图书管理领域&#xff0c;数字化管理系统可以有效地提高管理效率&#xff0c;提供更好的用户体验。本项目旨在开发一个基于Spring…

静态反射C++枚举名字的超简方案——C++闲得慌系列之(一)

C 有枚举&#xff0c;编译后其值就被转成整数了&#xff0c;有时程序会有输出枚举名字的需求&#xff0c;朴素的做法就是手工一个个写字符串&#xff08;名字&#xff09;&#xff0c;并实现匹配&#xff0c;比如&#xff1a; enum class Shape {rectangle, circular}; std::s…

再获信通院认可!华住&持安零信任项目获评零信任最佳方案奖!

2023年7月12日&#xff0c;在中国通信标准化协会算网融合产业及标准推进委员会&#xff08;CCSA TC621&#xff09;在京组织召开“2023算网融合产业发展峰会-零信任产业发展论坛”上&#xff0c;“2022零信任优秀案例”成果正式发布。 持安科技与华住集团共同完成的 “华住集团…

【SpringBoot】| Spring Boot 常见的底层注解剖析

目录 一&#xff1a;Spring Boot 常见的底层注解 1. 容器功能 1.1 组件添加 方法一&#xff1a;使用Configuration注解Bean注解 方法二&#xff1a;使用Configuration注解Import注解 方法三&#xff1a;使用Configuration注解Conditional注解 1.2 原生xml配置文件引入…

ITDR何以成为IAM的最佳搭档?

摘 要 ❖随着零信任方案的逐渐落地&#xff0c;身份成为企业的新边界。同时&#xff0c;身份基础设施成为了攻击焦点。 ❖最近的身份攻击变得更加巧妙和复杂&#xff0c;甚至可以绕过MFA。 ❖当前的IAM解决方案只能起到预防作用。 ❖企业更需要一个能够检测和响应身份威胁的…

Mysql 备份与还原

目录 一 数据库备份的重要性与分类 1.数据备份的重要性 2. 造成数据丢失的原因 3.从物理与逻辑的角度&#xff0c;备份分为 4.从数据库的备份策略角度&#xff0c;备份可分为 二、常见的备份方法 1.物理冷备 2.专用备份工具mydump或mysqlhotcopy 3.启用二进制日志进行增量…

上手vue2的学习笔记5之在vue2项目中调用elment-ui

前言 上手vue2的学习笔记4之搭建vue环境 参考链接&#xff1a;vue2.0项目引入element-ui 一、安装elment-ui 进入搭建的vue项目中 cd vue_bing_test 安装 element npm i element-ui二、引入elment-ui elment官方教程 将main.js改成如下内容&#xff1a; import Vue fro…

详解C语言自定义类型

目录 一&#xff0c;结构体 1.结构体的基础知识 2.结构体的声明 一般的声明 ‍特殊的声明&#xff08;匿名结构体类型&#xff09; 3.结构体的自引用 4.结构体变量的定义和初始化 ‍结构体变量的定义 结构体变量的初始化 结构体变量的嵌套初始化 5.结构体内存对齐 …

240-960MHz带编码器的单片OOK 发射器CMT2157B

CMT2157B 是一款真正意义上的单芯片、高灵活性、超低功耗、带编码器的OOK 射频发射芯片&#xff0c;非常适合于240 至960 MHz 的无线应用场 合。该芯片可实现完全兼容市面上最常用的527、1527、2262 和2240 等编码格式。此外&#xff0c;还支持用户各种自定义编码。该芯片支持4…