Typescript高级: 深入理解in与in keyof

概述

  • inkeyof 是两个非常重要的操作符,它们允许开发者对对象的键(key)进行更精细化的操作和约束
  • in 关键词
    • in关键词则在TypeScript的类型上下文中有特定的用途,它用于映射类型和类型查询
    • 当与keyof一起使用时,in可以遍历一个类型的所有键,并在类型层次上进行操作
    • 比如创建新的类型映射类型、条件判断或是泛型约束
  • keyof 关键词
    • keyof T是一个类型查询操作符,其中T是一个类型
    • 它返回一个联合类型,该联合类型包含了T所有公开属性的键(即属性名)
    • 这在TypeScript中非常重要,因为它允许我们动态地基于一个类型来获取它的属性名集合
    • 从而进行进一步的类型操作
  • 应用场景
    • 动态属性访问:在处理对象时,利用K in keyof T可以确保安全地遍历所有可能的属性,而不会引发运行时错误
    • 类型转换:Readonly类型别名,将所有属性转为只读,是映射类型的一种经典应用
    • API设计:在API设计中,通过K extends keyof约束泛型参数,可以精确地指定参数必须是对象的某个属性,增强了API的类型安全和易用性
    • 配置管理:动态配置对象时,利用映射类型和条件类型可以方便地实现配置项的默认值设定、类型转换等,保持代码的灵活性和可维护性

in 操作符

  • 在 TypeScript 中,in 操作符主要在类型守卫(type guard)和映射类型(mapped type)的上下文中使用

1 ) 类型守卫中的 in

  • 在类型守卫中,in 用于检查一个对象是否具有特定的属性
  • 这有助于在运行时确定对象的形状,并据此进行条件处理

示例

function processObject(obj: any) {  if ('name' in obj && 'age' in obj) {  console.log(`Name: ${obj.name}, Age: ${obj.age}`);  } else {  console.log('Object does not have name and age properties.');  }  
}
  • 在这个例子中,我们使用 in 操作符来检查 obj 是否具有 name 和 age 属性

2 )映射类型中的 in

  • 在映射类型中,in 用于遍历一个类型的所有属性键,并对每个属性进行转换
  • 这允许开发者创建一个新的类型,该类型基于现有类型的每个属性进行某种变换
  • 例如,我们可以定义一个映射类型,将 Person 类型的所有属性都变为可选:

示例

type PartialPerson = { [K in keyof Person]?: Person[K] };
  • 在这个例子中,PartialPerson 类型将 Person 类型的所有属性都标记为可选
  • 这是通过使用映射类型并结合 keyof 和 in 操作符来实现的
  • 对于 Person 类型的每一个键 K,我们都定义了一个新的可选属性
  • 其类型与原始 Person 类型中对应属性的类型相同

keyof 操作符

  • keyof 操作符用于获取对象类型的所有键(key)的联合类型(union type)
  • 这意味着,如果你有一个对象类型
  • 你可以使用 keyof 来得到一个包含该对象所有键名称的字符串字面量类型的联合

示例

type Person = {  name: string;  age: number;  address: {  city: string;  state: string;  };  
};  type PersonKeys = keyof Person; // "name" | "age" | "address"
  • 在这个例子中,PersonKeys 类型是一个联合类型
  • 包含了 “name”, “age”, 和 “address” 这三个字符串字面量类型

in 和 keyof 联合

  • in 和 keyof 可以结合使用,以实现更复杂的类型操作
  • 例如,你可以定义一个类型,该类型只包含 Person 中字符串类型的属性

示例

type Person = {    name: string;    age: number;    address: {    city: string;    state: string;    };  isStudent: boolean; // 添加一个非字符串类型的属性作为示例  
};  // 使用映射类型和条件类型来提取字符串类型的属性键  
type StringProps = { [K in keyof Person]: Person[K] extends string ? K : never }[keyof Person];  // 提取出来的 StringProps 类型应该只包含 'name'  
// 因为只有 'name' 属性的类型是 string  // 示例:使用 StringProps 类型  
let stringProp: StringProps;  
stringProp = 'name'; // 正确,因为 'name' 是 Person 类型中唯一的字符串属性  
// stringProp = 'age'; // 错误,因为 'age' 不是字符串类型  
// stringProp = 'isStudent'; // 错误,因为 'isStudent' 也不是字符串类型  // 展示 never 类型的示例  
// 尝试将非字符串类型的属性键赋值给 StringProps 类型的变量会导致编译错误  
type NonStringProp = Exclude<keyof Person, StringProps>; // 这会得到 'age' | 'address' | 'isStudent'  let nonStringProp: NonStringProp;  
nonStringProp = 'age'; // 正确,因为 'age' 是非字符串属性  
nonStringProp = 'isStudent'; // 正确,'isStudent' 也是非字符串属性  
// nonStringProp = 'name'; // 错误,因为 'name' 是字符串类型  // 使用一个辅助类型来尝试将 Person 的每个属性都转换为 StringProps,非字符串类型的属性会被转换为 never  
type AttemptStringConversion<K extends keyof Person> = Person[K] extends string ? K : never;// 示例:'age' 属性不是字符串,所以它的类型是 never  
let ageAsStringProp: AttemptStringConversion<'age'>; // 类型是 never  
// 下面这行代码会导致编译错误,因为我们不能将任何值赋给 never 类型的变量  
// ageAsStringProp = 'any value'; // 错误,不能将类型“string”分配给类型“never”  // 正确的使用方式是,不直接给 never 类型的变量赋值  
// 而是使用类型守卫或其他逻辑来避免处理 never 类型的情况
  • 在这个例子中,首先定义了一个映射类型,该类型遍历 Person 的所有属性
  • 对于每个属性 K,如果其类型是字符串,则保留该属性的键名;否则,将其替换为 never 类型
  • 然后,我们通过索引这个映射类型与 keyof Person 的结果
  • 得到了一个包含所有字符串类型属性键的联合类型
  • 其他,参考上述注释

基于 in keyof 实现轻量级 Map

class LightweightMap<K extends string | number | symbol, V> {  private data: Record<K, V> = {} as Record<K, V>;  set(key: K, value: V): void {  this.data[key] = value;  }  get(key: K): V | undefined {  return this.data[key];  }  has(key: K): boolean {  return key in this.data;  }  delete(key: K): boolean {  const hadKey = this.has(key);  if (hadKey) {  delete this.data[key];  }  return hadKey;  }  clear(): void {  this.data = {} as Record<K, V>;  }  size(): number {  return Object.keys(this.data).length;  }  // 可选:实现遍历功能  forEach(callback: (value: V, key: K, map: this) => void): void {  for (const key in this.data) {  if (this.data.hasOwnProperty(key)) {  callback(this.data[key], key as K, this);  }  }  }  
}  // 使用示例  
const map = new LightweightMap<string, number>();  
map.set('one', 1);  
map.set('two', 2);  console.log(map.get('one')); // 输出: 1  
console.log(map.has('two')); // 输出: true  map.forEach((value, key, mapInstance) => {  console.log(`Key: ${key}, Value: ${value}`);  
});  map.delete('two');  
console.log(map.has('two')); // 输出: false  console.log(map.size()); // 输出: 1  
map.clear();  
console.log(map.size()); // 输出: 0
  • 上述代码定义了一个名为 LightweightMap 的泛型类
  • 该类模拟了标准 Map 数据结构的一些基本功能,但是以一种更轻量级的方式实现
  • 这个类使用了 TypeScript 的泛型特性来提供类型安全
  • 允许用户为键(K)和值(V)指定类型
  • 注意事项和改进点
    • 性能: 对于大型数据结构,使用 Object.keys(this.data).length 来计算大小可能会影响性能。可以考虑维护一个内部计数器来跟踪大小,以减少计算开销。
    • 类型安全: 虽然 LightweightMap 提供了类型安全的键值对存储,但在实际使用中仍需注意确保提供给 set 方法的键是唯一的,以避免意外覆盖现有值。
    • 错误处理: 当前实现中没有显式的错误处理机制。在实际应用中,可能需要添加错误处理逻辑来处理例如尝试获取不存在的键或删除不存在的键等情况。
    • 扩展性: 根据具体需求,可以考虑为 LightweightMap 类添加更多功能,如 keys()、values()、entries() 等方法,以便更灵活地处理键值对。

结论

  • inkeyof 是 TypeScript 类型系统中的强大工具
  • 它们允许开发者对对象的键进行精细化的操作和约束
  • 通过结合使用这两个操作符,开发者可以定义出更加灵活和强大的类型
  • 从而提高代码的可读性和健壮性
  • in与keyof的结合,是TypeScript类型系统中的精华
  • 它们的配合使用极大地拓展了类型系统的边界
  • 为开发者提供了前所未有的类型操纵能力
  • 通过映射类型、条件判断、泛型约束等功能,开发者能写出更安全、灵活、可维护的代码

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

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

相关文章

派生类中调用基类的__init__()方法

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 在派生类中定义__init__()方法时&#xff0c;不会自动调用基类的__init__()方法。例如&#xff0c;定义一个Fruit类&#xff0c;在__init__()方法中创…

【安卓基础】-- 消息机制 Handler

目录 消息机制 Handler面试问题 消息机制 Handler 对handler机制的基本作用、用法、时序流程进行介绍&#xff0c;针对handler机制中的内存泄漏问题讲解&#xff1a;一篇读懂Android Handler机制 Android-Handler机制详解 全面解析 | Android之Handler机制 需要掌握的&#x…

通过非欧几何体改变 AI 嵌入

目录 一、说明 二、LLM嵌入的形势 三、了解一些背景信息 3.1 什么是嵌入&#xff1f; 3.2 为什么嵌入在 NLP 中很重要&#xff1f; 3.3 复数Complex 几何的角色 3.4 C主动学习 3.5 角度嵌入 &#xff08;AE&#xff09;&#xff1a;解锁稳健排序 3.6 RotatE&#xff1a;将关系…

浅谈序列化

序列化的基本概念 序列化&#xff08;Serialization&#xff09;是将对象的状态转换为字节流的过程&#xff0c;而反序列化&#xff08;Deserialization&#xff09;则是将字节流恢复为对象的过程。这一过程允许对象能够被保存到文件、传输通过网络、保存到数据库或其他数据存…

如何高效地向Redis 6插入亿级别的数据

如何高效地向Redis插入亿级别的数据 背景不可用的方案可用方案:利用管道插入其他命令&#xff1a;参考&#xff1a; 背景 上一条记录&#xff1b;80G的存储&#xff1b;10几个文件&#xff0c;如何快速导入是一个大问题&#xff0c;也是一个很棘手的问题&#xff1b;如下将给出…

day-37 最大正方形

思路 动态规划&#xff0c;这题主要得弄明白状态转换方程&#xff0c;dp[i][j]表示以&#xff08;i,j&#xff09;为右下角的最大正方形 解题方法 1.首先将第一行和第一列初始化&#xff0c;当对应位置的matrix为’0’时&#xff0c;dp数组对应位置也为零&#xff0c;否则为1 …

【工具】探索 DOU:每用户数据使用量

缘分让我们相遇乱世以外 命运却要我们危难中相爱 也许未来遥远在光年之外 我愿守候未知里为你等待 我没想到为了你我能疯狂到 山崩海啸没有你根本不想逃 我的大脑为了你已经疯狂到 脉搏心跳没有你根本不重要 &#x1f3b5; 邓紫棋《光年之外》 什么是 DOU…

重学java 55. 集合 Set接口

我救自己万万次&#xff0c;铮铮劲草&#xff0c;绝不动摇 —— 24.6.2 一、Set集合介绍 Set和Map密切相关的 Map的遍历需要先变成单列集合&#xff0c;只能变成set集合 二、HashSet集合的介绍和使用 1.概述 HashSet是Set接口的实现类 2.特点 a、元素唯一 b、元素无序 c、无索引…

前端面试题日常练-day48 【面试题】

题目 希望这些选择题能够帮助您进行前端面试的准备&#xff0c;答案在文末 1. 在Bootstrap中&#xff0c;以下哪个类用于创建一个具有响应式的导航栏&#xff1f; a) .navbar-responsive b) .responsive-nav c) .navbar-collapse d) .nav-responsive 2. 哪个Bootstrap类用于…

开源VS闭源:大模型之争,究竟谁更胜一筹?

随着人工智能技术的快速发展&#xff0c;大模型作为其中的核心组件&#xff0c;已经引起了业界的广泛关注。在大模型的研发过程中&#xff0c;开源与闭源成为了两个备受争议的话题。究竟开源与闭源谁更好&#xff1f;本文将从多个角度进行深入分析&#xff0c;为大家揭示真相。…

Windows API 编程

Windows API 函数大全 (推荐)&#xff1a;https://blog.csdn.net/xiao_yi_xiao/article/details/121604742Windows API 在线参考手册&#xff1a;http://www.office-cn.net/t/api/index.html?web.htmWindows 开发文档 (官方)&#xff1a;https://learn.microsoft.com/zh-cn/wi…

如何把图片保存成16位png格式?

在进行图像处理的过程中&#xff0c;见过8位和24位的图片&#xff0c;然而还没见过16位的&#xff0c;其实也有&#xff0c;比如对于灰度图&#xff0c;就是相当于利用65535个灰度级进行灰度存储。而8位就是256个位置存储。相当于就是0-255. 今天尝试了巨久&#xff0c;用pyth…

重载运算符C++---学习笔记

一、笔记 1. 重载运算符基础知识 重载运算符进行的运算和普通数的加减运算不同之处在于重载运算符的操作数为一个一个自定义的对象&#xff0c;所以相应的要对普通的运算符如-*%/的调用方法进行重写&#xff0c;重载的本质还是函数调用 2. 重载运算符的语法 重载运算符的语…

Kubernetes-使用集群CA证书给用户颁发客户端证书访问Api-Server

一、官网地址 证书和证书签名请求 | Kubernetes 二、Demo 一、创建测试文件夹 cd ~ mkdir add_k8s_user_demo cd add_k8s_user_demo 二、创建符合X509标准的证书 openssl genrsa -out myuser.key 2048 openssl req -new -key myuser.key -out myuser.csr -subj "/CNmy…

自动微分技术在 AI for science 中的应用

本文简记我在学习自动微分相关技术时遇到的知识点。 反向传播和自动微分 以 NN 为代表的深度学习技术展现出了强大的参数拟合能力&#xff0c;人们通过堆叠固定的 layer 就能轻松设计出满足要求的参数拟合器。 例如&#xff0c;大部分图神经网络均基于消息传递的架构。在推理…

Jitsi meet 退出房间后,用户还在房间内

前言 Jitsi Meet 如果客户端非正常退出会议&#xff0c;会产生用户还在房间内&#xff0c;实际用户已经退出的情况&#xff0c;需要一段时间内&#xff0c;才会在UI离开房间&#xff0c;虽然影响不大&#xff0c;但是也容易导致体验不好。 保活 Jitsi Meet 会和前端做一个保…

QT中如何将对第三方库进行翻译

1、背景 在我们的程序中,可能会加载其他人写的模块,,该模块是以库的形式提供的,那么我们程序翻译时,如何来对引入的第三方库进行翻译??? 2、方案 首先,第三方库会有自己的翻译文件,并且一般要给我们提供设置翻译的接口, 例如下:第三方库给我们暴露一个接口,我们…

带交互的卡尔曼滤滤波|一维滤波|源代码

背景 一维卡尔曼滤波的MATLAB例程&#xff0c;​背景为温度估计。 代码介绍 运行程序后&#xff0c;可以自己输入温度真实值&#xff1a; 以20℃为例&#xff0c;得到如下的估计值​&#xff1a; 滤波前的值和滤波后的值分别于期望值&#xff08;真实值&#xff09;作差…

基于Jenkins+Kubernetes+GitLab+Harbor构建CICD平台

1. 实验环境 1.1 k8s环境 1&#xff09;Kubernetes 集群版本是 1.20.6 2&#xff09;k8s控制节点&#xff1a; IP&#xff1a;192.168.140.130 主机名&#xff1a;k8s-master 配置&#xff1a;4C6G 3&#xff09;k8s工作节点 节点1&#xff1a; IP&#xff1a;192.1…

lucene 9.10向量检索基本用法

Lucene 9.10 中的 KnnFloatVectorQuery 是用来执行最近邻&#xff08;k-Nearest Neighbors&#xff0c;kNN&#xff09;搜索的查询类&#xff0c;它可以在一个字段中搜索与目标向量最相似的k个向量。以下是 KnnFloatVectorQuery 的基本用法和代码示例。 1. 索引向量字段 首先…