TypeScript学习笔记 | 泛型 - 泛型的应用 - 泛型工具类 - extends - infer用法

news2024/9/22 11:32:35

文章目录

  • TypeScript学习笔记 | 泛型 - 泛型的应用
    • 什么是泛型
      • 类型兼容
        • 鸭子类型
          • 类型运算「&」和「|」
      • 泛型的定义
      • 泛型的使用
        • 泛型接口与泛型类
      • extends用法
        • 泛型用法
          • 条件判断 用于类型的条件判断
          • 应用:Exclude<T,U>排除 / Extract 提取
      • infer关键词 类型推导
      • keyof 和 in
      • 泛型约束
        • 确保属性的存在
        • 检查对象上的键是否存在
      • TypeScript映射类型
        • 映射类型修饰符
      • 泛型工具类
        • Partial:将类型里的属性全部变为可选项?
        • Record:转换类型对象中所有属性的类型
        • Pick:挑选某类型的子属性

TypeScript学习笔记 | 泛型 - 泛型的应用

参考文章:https://juejin.cn/post/6844904184894980104

什么是泛型

泛型指是在定义函数或者类时并不知道类型,在被使用时才知道数据的类型。
泛型先起到一个占位的作用,在使用时接收传递过来的类型。

也可以将泛型类比于参数,参数接收的是变量值,泛型接收的是类型。

类型兼容

研究 TypeScript 的关键问题就是讨论两个类型之间的兼容性,类型之间的关系简单分为三种

  • A 是 B 的子类型
  • A 是 B 的超类型
  • A 和 B 不兼容

子类型与子类不同,仅描述两个类型之间的兼容性,且子类型是包括自身的,就像集合子集也包括自己一样。
作用:保证属性是存在

// 比如 Cat 是  Animal 的子类型,那么类型为 Cat  的变量就可以赋给类型为 Animal  的变量了
// Cat 可以保证有 Animal 上的属性,反之不成立
let b : Animal;
let a : Cat;
b = a ; // 正确
a = b ; // 错误
鸭子类型

鸭子类型是 TypeScript 类型的最大特性:仅关注对象上的属性和方法,而不关注继承关系
鸭子类型:只关心属性和行为是否一样,并不关心是不是具体对应的类型(走得像鸭子声音像鸭子就是鸭子)

Class Duck{ swim } 
Class Dog{ swim,bark }

在 TypeScript 中 Dog 是 Duck 的子类型,因为它满足了 Duck 上的所有方法和属性。

类型运算「&」和「|」

在这里插入图片描述
在这里插入图片描述

  • &:求两个类型的并集,同名属性也对其类型 &
  • |:求两个类型的交集,只保留同名属性且也对其类型

泛型的定义

语法:<类型变量>
在这里插入图片描述
T被称为类型变量(type variable),T作为参数泛化成具体类型的作用叫做类型参数化。

说明
1.泛型和变量一样,可以指定多个。比如<T , U>,中间使用逗号隔开。
2.泛型和变量一样,可以指定默认值。比如< T = string>
3.泛型定义的位置,定义的位置一般在标识符的后面。比如function fn <T> (a:T):T{}

泛型的使用

显示设置:像传递函数一样传递类型给泛型变量。(推荐,更方便阅读)
隐式设置:根据参数类型自动推断类型。(更简洁)

// 显示设置
function identity <T, U>(value: T, message: U) : T {
  console.log(message);
  return value;
}

console.log(identity<number, string>(68, "Semlinker"));

//隐式设置
function identity <T, U>(value: T, message: U) : T {
  console.log(message);
  return value;
}

console.log(identity(68, "Semlinker"));

在这里插入图片描述

泛型接口与泛型类

泛型接口案例

//泛型接口的定义
interface Identities<V, M> {
  value: V,
  message: M
}

//泛型接口的使用
function identity<T, U> (value: T, message: U): Identities<T, U> {
  console.log(value + ": " + typeof (value));
  console.log(message + ": " + typeof (message));
  let identities: Identities<T, U> = {
    value,
    message
  };
  return identities;
}

console.log(identity(68, "Semlinker"));

泛型类案例

class Point<T> {
  x: T
  y: T
  z: T
  constructor(x: T, y: T, z: T) {
    this.x = x
    this.y = y
    this.z = z
  }
}

const p1 = new Point('1.33.2', '2.22.3', '4.22.1')
const p2 = new Point<string>('1.33.2', '2.22.3', '4.22.1')
const p3: Point<string> = new Point('1.33.2', '2.22.3', '4.22.1')

extends用法

  • 接口继承:类似于ES6的Class语法中类的继承。
  • 条件判断:类似于JS的三元表达式

接口继承的用法

interface T1 {
    name: string
  }
  
  interface T2 {
    sex: number
  }
  
  // 多重继承,逗号隔开
  // T3 接口继承于T1,T2接口,T3是T1和T2的子接口,T3接口的要求更多。
  interface T3 extends T1,T2 {
    age: number
  }
  
  // 合法
  const t3: T3 = {
    name: 'xiaoming',
    sex: 1,
    age: 18
  }
泛型用法
// 普通用法
type A1 = 'x' extends 'x' ? string : number; // string
type A2 = 'x' | 'y' extends 'x' ? string : number; // number

// 泛型用法  
type P<T> = T extends 'x' ? string : number;
type A3 = P<'x' | 'y'> // ?

感觉上A2和A3是一样的,但其实A3的类型是 string | number
原因:如果extends前面的参数是一个泛型类型,当传入该参数的是联合类型,则使用分配律计算最终的结果。也就是将联合类型分别拆开带入泛型,最后将结果再联合起来。

特殊的never
never被认为是没有联合项的联合类型,所以还是满足分配律,然而因为没有联合项可以分配,所以P<T>的表达式根本没有执行,所以A2的定义也就类似于永远没有返回的函数一样,是never类型的。

  // never是所有类型的子类型
  type A1 = never extends 'x' ? string : number; // string
  type P<T> = T extends 'x' ? string : number;
  type A2 = P<never> // never

阻止条件判断中的分配律
如果我们希望将联合类型看成一个整体,不适用分配律,那么在条件判断类型的定义中,将泛型参数使用[]括起来。

下面的例子,传入参数T的类型将被当作一个整体。

  type P<T> = [T] extends ['x'] ? string : number;
  type A1 = P<'x' | 'y'> // number
  type A2 = P<never> // string
条件判断 用于类型的条件判断

extends判断条件的逻辑:如果extends前面的类型可以直接赋值给extends后面的类,那么表达式为真

下面的示例中,DogAnimal的子类型,父类型比子类型的限制更少。能满足子类型,则一定能满足父类型。
Dog类型的值可以赋值给Animal类型的值,判断为真。

可以简单理解为,满足前面类型的数据是否能满足后面的类型。

  // 示例1
  interface Animal {
    eat(): void
  }
  
  interface Dog extends Animal {
    bite(): void
  }
  
  // 如果问号前面的判断为真,则将第一个类型string赋值给A,否则将第二个类型number赋值给A。
  type A = Dog extends Animal ? string : number
  
  const a: A = 'this is string'
应用:Exclude<T,U>排除 / Extract 提取

Exclude<T,U> 排除

语法定义:type Exclude<T, U> = T extends U ? never : T
作用:其作用是从第一个联合类型参数中,将第二个联合类型中出现的联合项全部排除,只留下没有出现过的参数。

示例

type A = Exclude<'key1' | 'key2', 'key2'> // 'key1'
type Exclude<T, U> = T extends U ? never : T

//T为'key1' | 'key2',U为'key2' extends前面的类型是一个泛型,并且是一个联合类型,分配律奏效
type A = ('key1' extends 'key2' ? never : 'key1') | ('key2' extends 'key2' ? never : 'key2')

// =>
type A = 'key1' | never

//never是所有类型的子类型
type A = 'key1'

Extract<T,U> 提取
原理:type Extract<T, U> = T extends U ? T : never
作用:将第二个参数的联合项从第一个参数的联合项中提取出来。(第二个参数可以含有第一个参数没有的项)

type A = Extract<'key1' | 'key2', 'key2'> // 'key2'
type Extract<T, U> = T extends U ? T : never

// T为'key1' | 'key2',U为'key2' extends前面的类型是一个泛型,并且是一个联合类型,分配律奏效
type A = ('key1' extends 'key2' ? 'key1' : never) | ('key2' extends 'key2' ? 'key2' : never)

// =>
type A = never | 'key2'

//never是所有类型的子类型
type A = 'key2'

infer关键词 类型推导

作用:在类型未推导时进行占位,等到真正推导成功后,能准确地返回正确类型。
比如:条件语句 T extends (...args: infer P) => any ? P : T 中,infer P 表示待推断的函数参数。

infer只能在 extends 条件语句中使用,声明变量只能在true分支中使用

// numberPromise 是否可以赋值给 Promise<infer P>  P占位,如果可以则n = P推导出来的类型
type numberPromise = Promise<number>;
type n = numberPromise extends Promise<infer P> ? P : never; // number

keyof 和 in

  • keyof 类型 接受一个对象类型作为参数,返回该对象属性名组成的字面量联合类型。通过 keyof 操作符,可以获取指定类型的所有键
  • in的右侧一般会跟一个联合类型,使用in操作符可以对该联合类型进行迭代。 其作用类似JS中的for...in或者for...of
//keyof使用案例
type Dog = { name: string; age: number;  };
type D = keyof Dog; //type D = "name" | "age"

//in的使用案例
type Animals = 'pig' | 'cat' | 'dog'​
type animals = {
    [key in Animals]: string
}
// type animals = {
//     pig: string; //第一次迭代
//     cat: string; //第二次迭代
//     dog: string; //第三次迭代
// }
  • keyof any返回联合类型string | number | symbol

补充:遇见索引签名时,keyof会直接返回索引的类型。索引类型为string时,keyof返回的类型是string | number,这是因为数字类型索引最终访问时也会被转换为字符串索引类型。

  type Dog = {  [y:number]: number  };
  type dog = keyof Dog;  //type dog = number
  ​
  type Doggy = {  [y:string]: boolean };
  type doggy = keyof Doggy; //type doggy = string | number

使用场景:与extends关键字结合使用,对对象的属性做限定。

泛型约束

对泛型的类型进行约束

确保属性的存在

使用场景:希望变量的类型上存在某属性

interface Length {
  length: number; //这个是特殊用法,表示该变量只要有length就可以,有多余的属性也可以
}
//T extends Length 用于告诉编译器,支持已经实现 Length 接口的任何类型。 接口的继承用法
function identity<T extends Length>(arg: T): T {
  console.log(arg.length); // 可以获取length属性
  return arg;
}
检查对象上的键是否存在

extendskeyof 结合限定类型建 ,表示extends 前面的类型是keyof返回的联合类型中的一个。

extends 不一定要强制满足继承关系,检查是否满足结构兼容性。

// k extends  "name"| "age"  这里确保参数 key 一定是对象中含有的键
function getObjectProperty<O,K extends keyof O>(obj:O,key:K){
    return obj[key]
} 
const info = {
    name:"ranran",
    age:18
}

getObjectProperty(info,"name")

TypeScript映射类型

映射类型:可以理解为将一个类型映射成一个新的类型
使用场景:一个类型需要另一个类型,但是又不想拷贝(映射)一份,可以考虑使用映射类型。

可以将映射类型想象成一个函数,函数的作用就是拷贝(映射)类型

//映射类型MapPerson
//<>定义泛型,接收需要拷贝的类型  
type MapPerson<Type> = {
    //[index:number]:any //索引签名写法
    //Property自定义的标识符, keyof Type表示联合类型,in从联合类型中依次取值赋值给Property
    [Property in keyof Type]:Type[Property]
}
interface Iperson{
    name:string
    age:number
}

type NewPerson = MapPerson<Iperson> //使用映射类型拷贝Iperson类型
映射类型修饰符
  • readonliy 设置属性只读
  • 设置属性可选

可以通过前缀-或者+删除或添加这些修饰符,默认使用+

//映射类型MapPerson
//将所有属性映射为可选属性
type MapPerson<Type> = {
   +readonly [Property in keyof Type]:Type[Property]
}

interface Iperson{
    name:string
    age:number
}

type NewPerson = MapPerson<Iperson>
/*
type NewPerson = {
    readonly name: string;
    readonly age: number;
}
*/

泛型工具类

Partial:将类型里的属性全部变为可选项?

Partial<T> 的作用就是将某个类型里的属性全部变为可选项 ?

Partial<T>的实现可以看成映射类型 + 可选?修饰符

// P为key T为类型对象
//通过 keyof T 拿到 T 的所有属性名,然后使用 in 进行遍历,将值赋给 P,最后通过 T[P] 取得相应的属性值。中间的 ? 号,用于将所有属性变为可选。
type Partial<T> = {
    [P in keyof T]?: T[P];
};
Record:转换类型对象中所有属性的类型

Record<K extends keyof any, T> K 中所有属性的值转化为 T 类型

  • K:创建的新对象需要有哪些属性,属性可以只有一个,也可以有多个,多个属性时采用“联合类型”的写法
    • keyof any返回联合类型string | number | symbolK extends keyof any 表示的是泛型参数 K 必须是一个能作为对象键的类型。
  • T:对象属性的类型。

定义

//[P in K] 表示的是:遍历 K 中的每个键,并将其作为属性名,然后将类型 T 分配给每个属性。
type Record<K extends keyof any, T> = {
    [P in K]: T;
};

示例

interface PageInfo {
  title: string;
}

type Page = "home" | "about" | "contact";
/*
Record<Page, PageInfo>返回
{
  about: { title: string},
  contact: { title: string},
  home: { title: string}
}
*/


const x: Record<Page, PageInfo> = {
  about: { title: '任意字符串'},
  contact: { title: '任意字符串'},
  home: { title: '任意字符串'}
};

使用场景
创建一个特定key并且值类型一样的对象

interface IPerson {
  name: string;
  age: number;
  email: string;
  gender: string;
};

type Person = Record<keyof IPerson, string>

const person: Person = {
  name: "Echo",
  age: "26",
  email: "test@123.com",
  gender: "Male",
};

注意点
1.属性名唯一,如果重复了,后面的键值会覆盖前面的
2.Record<K, T> 创建的对象类型只能包含指定键的属性,不允许存在其他未定义的属性。
3.Record<K, T> 创建的对象类型,如果键包含可选属性,生成的新类型的属性都是必选的。
4.所有属性的属性值都应该具有相同的类型 T,否则 TypeScript 编译器会报错。

Pick:挑选某类型的子属性

Pick<T, K extends keyof T>将某个类型中的子属性挑出来

定义

type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
};

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

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

相关文章

【23真题】这套适合考211的同学练手!考察复杂梅森!

今天分享的是23年五邑大学801的信号与系统试题及解析。 本套试卷难度分析&#xff1a;本套试题难度中等偏下&#xff0c;考察的题量较少&#xff0c;但是涉及到的知识点是非常全面的&#xff0c;考察的知识大多都是三大性质的定义以及运用&#xff0c;还考察了复杂梅森公式的运…

使用d3-tip报错:targetel.getScreenCTM is not a function

问题背景&#xff1a;使用d3-tip悬浮不显示信息 控制台报错截图&#xff1a; 报错原因&#xff1a; 箭头函数this 指向问题 解决方案&#xff1a;

无缝的链间互操作性:通用消息传递的强大之处

前言 通用消息传递&#xff08;General Message Passing&#xff0c;GMP&#xff09;是一种支持区块链之间通信和数据传输的机制。GMP正在成为增强不同区块链网络之间互操作性的解决方案。GMP允许应用程序构建者通过使用安全消息在区块链之间通信和交换信息来利用任何区块链的…

node-sass安装失败

在重新下载Vue工程依赖的时候&#xff0c;一直提示我报错&#xff0c;报错信息和截图如下&#xff0c;解决办法放在下面&#xff0c;供大家参考。 ValueError: invalid mode: rU while trying to load binding.gyp gyp ERR! configure error gyp ERR! stack Error: gyp faile…

浅谈余压监控系统电气设计

安科瑞 华楠 摘 要&#xff1a;结合实际的工程设计案例&#xff0c;分析余压监控系统的设计&#xff0c;包括余压探测器、余压控制器、余压监控主机的控制原理等。防止人员在实际的火灾疏散过程中会出现楼梯间和前室之间、前室和室内走道之间防火门两侧压差过大&#xff0c;而…

vs2022 使用git同步报错以及解决每次推送要输入密码问题

1.使用 git GUI工具&#xff0c;例如&#xff1a;TortoiseGit &#xff0c;把全局配置文件这样设置一下 设置全局.config ,这样即可。 [credential] helper store 2.如果推送代码或拉取代码一直失败&#xff0c;在当前的仓库下面&#xff0c;使用以下命令来重置一下密码 git …

Java 将list集合的字符串格式转为Map

Java 将list集合的字符串格式转为Map List<Object> list new ArrayList<>(); Map<String,String> map1 new HashMap<>(); map1.put("fileName","测试1"); map1.put("level","1"); list.add(map1);Map<S…

ChineseChess2

中国象棋&#xff1a;黑将&#xff0c;红帅双炮&#xff0c;只要红帅中间露头怎么走怎么赢 卡主黑将的走位&#xff0c;控制住就好了 ChineseChess-CSDN博客

数字驱动,营销赋能丨工商职院电子商务专业学生,前往餐饮美食电商新业态基地试岗交流

纸上得来终觉浅&#xff0c;绝知此事要躬行。为了让学生更好的了解自己与所应聘岗位的匹配度&#xff0c;同时也希望在实际业务场景中&#xff0c;发掘自身优势&#xff0c;10月23日&#xff0c;四川产教融创园信息技术有限公司组织四川工商职业技术学院的电子商务专业学生一行…

医院陪诊系统源码 医院护工陪护系统源码

医院陪诊系统源码 医院护工陪护系统源码 基于ThinkPHP6原生微信小程序框架开发的陪诊小程序源码 支持多运营区&#xff0c;陪护师、推广者等完整闭环功能&#xff0c;快速搭建陪护业务平台。 运行环境&#xff1a;PHPMYSQLNGINX/APACHE微信小程序 功能介绍&#xff1a; 1、…

LLVM学习笔记(49)

4.1.3. 降级 在前面的章节里&#xff0c;我们展示了目标机器特定节点与目标机器无关节点共存的一个图。你可能会问&#xff0c;如果这是指令选择的一个输入&#xff0c;为什么在SelectionDAG类中已经有一些目标机器特定的节点&#xff1f;要理解这&#xff0c;我们首先在下图概…

已经是项目经理了,还有必要考PMP吗?

这个必要性不是一定的&#xff0c;如果你想升职加薪&#xff0c;想突破瓶颈&#xff0c;没有其它办法提升的话&#xff0c;考一个有针对性的项目管理证书也是有一些必要性的&#xff0c;虽然薪资和证书不是画等号的&#xff0c;但是多了一些可能性&#xff0c;既能提升自己&…

小白如何把微博的视频下载到本地,只需2个步骤

微博是中国最受欢迎的社交网络平台之一&#xff0c;用户可以在其上分享文字、图片和视频。但是&#xff0c;有时候我们会想要将喜欢的微博视频保存到本地&#xff0c;以便随时观看。本文将向您介绍如何实现这一目标。 无水印方法&#xff1a; 1. 搜一搜获取视频下载小助手 为…

ffmpeg推流 nginx-http-flv-module作为流媒体服务器转流m3u8 前端vue集成videojs-player播放 实现、调优、问题解决

背景&#xff1a; 最近公司要基于双目视觉做鹤管自动充装项目&#xff0c;需要对相机的流媒体进行推流&#xff0c;推流到流媒体服务器后&#xff0c;再将流转换成页面能播放的flv或m3u8格式的直播流&#xff0c;然后在页面进行视频直播。本项目共有四个鹤位&#xff0c;所以需…

1024 有奖征名|来给矩阵起源办公室的新猫取名字呀~

在一个风雨交加的夜晚&#xff0c;一只无名的小黑猫突然出现在矩阵起源办公室中。这只小黑猫的出现引起了员工们的惊讶和好奇。他的毛色乌黑亮丽&#xff0c;眼神炯炯有神&#xff0c;容貌精神焕发&#xff0c;脚步身轻如燕、叫声熠熠生辉。尽管大家都很喜欢这只可爱的小动物&a…

项目管理-2023西电网课课后习题答案-第二章

文章目录 第二章答案1-1011-2021-3031-4040-42 [✅] 第一章答案[✅] 第二章答案 第三章答案 第四章答案 第五章答案 第二章答案 1-10 11-20 21-30 31-40 40-42

【Redis系列】在Centos7上安装Redis5.0保姆级教程!

哈喽&#xff0c; 大家好&#xff0c;我是小浪。那么最近也是在忙秋招&#xff0c;很长一段时间没有更新文章啦&#xff0c;最近呢也是秋招闲下来&#xff0c;当然秋招结果也不是很理想&#xff0c;嗯……这里就不多说啦&#xff0c;回归正题&#xff0c;从今天开始我们就开始正…

横坐标日期等间隔绘图 python示例代码

有两列数据&#xff0c;一列是日期&#xff0c;另一列是数值。日期是递增的&#xff0c;但是间隔不是均匀的。比如1月1日至2月1日有10组数据&#xff0c;2月1日至3月1日有100组数据&#xff0c;3月1日至4月1日有1000组数据。我想绘折线图&#xff0c;横坐标是日期&#xff0c;纵…

内网电脑监控软件的功能

内网电脑监控软件是一种用于监控企业内部网络使用情况的软件&#xff0c;可以帮助企业管理者更好地了解员工的工作状态和网络使用情况&#xff0c;提高工作效率和信息安全。 其功能具体如下&#xff1a; 1、实时电脑屏幕 实时监控是内网电脑监控软件的核心功能之一&#xff0…