Typescript高级: 深入实践Record类型
概述
- Record 类型是TS中其众多强大特性之一
- 它为我们提供了创建键值对映射的强大能力
- 极大地增强了代码的灵活性与类型安全性
应用示例
1 )用于配置场景
- 在复杂的项目中,配置文件往往包含多个模块的不同设置
- 使用 Record 可以确保配置的键名正确且值类型统一
示例
interface AppConfig {port: number;env: 'development' | 'production';
}const config: Record<string, AppConfig> = {server: { port: 3000, env: 'development' },client: { port: 8080, env: 'production' },
};
2 )枚举场景
- Record 也常用于将枚举类型转换为易于使用的键值对映射,便于在代码中引用
示例
enum Color {Red,Green,Blue,
}const colorNameMap: Record<Color, string> = {[Color.Red]: 'Red',[Color.Green]: 'Green',[Color.Blue]: 'Blue',
};
3 )联合类型场景
示例
type Scores = Record<'Math' | 'English' | 'Science', number>; const studentScores: Scores = { Math: 90, English: 85, Science: 92
};
4 )表单项的配置
- 在构建动态表单时,Record 可以用来定义表单项的配置,每个表单项都有其特有的属性
示例
interface FormField {type: 'text' | 'checkbox' | 'select';label: string;required?: boolean;
}const formConfig: Record<string, FormField> = {username: { type: 'text', label: 'Username', required: true },agreeTerms: { type: 'checkbox', label: 'Agree to terms', required: false },
};
5 ) 映射类型类似Record
type Invert<T> = { [P in keyof T as T[P] extends string ? T[P] : never]: P;
}; type Original = { a: '1'; b: '2' };
type Inverted = Invert<Original>; // 测试反转类型
const invertedObj: Inverted = { '1': 'a', '2': 'b'
}; // 这是一个类型守卫函数,用于在运行时检查对象是否符合 Inverted 类型
function isInvertedType(obj: any): obj is Inverted { return '1' in obj && '2' in obj && obj['1'] === 'a' && obj['2'] === 'b';
} if (isInvertedType(invertedObj)) { console.log('invertedObj 符合 Inverted 类型');
} else { console.log('invertedObj 不符合 Inverted 类型');
}
Invert<T>
类型别名通过条件映射- 使用了as关键字的映射类型语法,这在TypeScript 4.1及以后版本中可用
- 来生成一个新的类型,该类型实质上是原类型T中每个键值对的倒置
- 但这种方式更加灵活,因为它基于值的类型来进行映射
- 在这个例子中,要求原对象的值必须是字符串,从而可以用作新类型的键
- 因此,虽然Inverted类型在功能上类似于某种Record类型的应用
- 但它更准确地描述为一个经过类型系统转换处理后得到的、具有特定键值对的对象类型,而不是直接等同于使用Record定义的类型
6 )自定义Record(硬编码联合类型映射)
示例
// 1. 使用 TypeScript 内置的 Record 类型
type UserRecord = Record<string, any>; // 允许任何字符串作为键,任何类型作为值 // 假设我们有一个具体的键集合和值类型
type SpecificKeys = 'username' | 'age';
type SpecificUserRecord = Record<SpecificKeys, string>; // 键必须是 'username' 或 'age',值必须是 string // 2. 自定义一个类似 Record 的类型,但键是硬编码的(这不是一个好的做法,因为它不够灵活)
type MyRecord<T> = { [P in 'username' | 'age']: T; // 硬编码的键 'username' 和 'age',值类型由泛型 T 指定
};// 使用自定义的 MyRecord 类型
type MyUserRecord = MyRecord<string>; // 键是 'username' 或 'age',值都是 string // 示例对象
const user: SpecificUserRecord = { username: 'Alice', age: '30' // 注意:这里为了演示使用了字符串,但在实际应用中年龄应该是数字
}; const myUser: MyUserRecord = { username: 'Bob', age: '25' // 同样,这里使用了字符串作为示例
}; // 说明:
// 1. 内置的 Record 类型更加灵活,允许您指定任何键的集合(K extends keyof any)和值的类型(T)。
// 2. 自定义的 MyRecord 类型在这里是硬编码的,它只允许 'username' 和 'age' 作为键,并且值的类型由泛型 T 指定。
// 3. 在实际应用中,通常推荐使用内置的 Record 类型,因为它更加灵活且易于维护。
7 ) 类型谓词与模式匹配
示例
function isRecordOfStrings(obj: any): obj is Record<string, string> {return typeof obj === 'object' && obj !== null && Object.values(obj).every(v => typeof v === 'string');
}if (isRecordOfStrings(someObj)) {// 在此块内,someObj 被认为是 Record<string, string>
}
- 通过类型谓词和 Record 结合,可以实现更加精准的类型判断和模式匹配逻辑
8 ) Record 与 索引签名
示例
// 假设我们有一个Goods类型
type Goods = { id: number; name: string;
}; // TypeScript内置的Record类型
type RecordType<K extends keyof any, T> = { [P in K]: T;
}; // 使用Record类型来创建一个新的类型,该类型的属性与Goods的键相同,但值都是string类型
type resultGoodsType = RecordType<keyof Goods, string>;
// 这等价于:
// type resultGoodsType = {
// id: string;
// name: string;
// }; // 类似索引签名的类型定义(但不是Record)
type IndexSignatureType = { [x: string]: any; // 字符串索引签名,允许任何字符串作为键,值是any类型 // 注意:一旦你定义了字符串索引签名,那么就不能再为特定的属性名定义更具体的类型了 // 因为所有属性名最终都会被视为字符串,并遵循这个索引签名的类型
}; // 索引签名不能包含number或symbol作为键类型(除了特殊的数组和Map类型)
// type BadIndexSignatureType = {
// [x: number]: any; // 错误:索引签名参数类型必须为 "string" 或 "number" 或 "symbol"。
// }; // 使用Record和索引签名的区别
// Record类型允许你明确指定键的类型和值的类型
// 而索引签名则提供了一种更通用的方式来描述对象的结构,但会限制你对特定键的类型定义 // 示例:使用Record和索引签名
const recordExample: resultGoodsType = { id: '123', // 符合resultGoodsType的定义,因为id现在是string类型 name: 'Apple' // 符合resultGoodsType的定义,因为name现在是string类型
}; const indexSignatureExample: IndexSignatureType = { id: 123, // 符合IndexSignatureType的定义,因为id被视为字符串键,但其值可以是any类型 name: 'Banana', // 符合IndexSignatureType的定义 'some-other-key': true // 也符合,因为所有字符串键都符合索引签名的定义
};
联系:
- Record类型和索引签名都用于描述对象的结构
- 它们都允许你使用类型参数来定义键和值的类型
区别:
- Record是一个内置工具类型,它接受两个类型参数来明确指定键和值的类型
- 索引签名是TypeScript中对象类型定义的一部分,它允许你定义一个或多个类型的键(通常是string或number),并为这些键指定一个值的类型
- 一旦你定义了一个字符串索引签名,那么你就不能再为对象的特定属性定义更具体的类型了(除非该类型与索引签名的类型兼容)
- 这是因为所有属性名最终都会被视为字符串,并遵循这个索引签名的类型。
- Record类型提供了一种更精确和可控的方式来定义对象的结构,而索引签名则提供了一种更通用和灵活的方式来描述对象的结构
9 )实现轻量级 Map
type SimpleMap<KeyType extends string | number | symbol, ValueType> = {[key in KeyType]: ValueType;
};class LightMap<KeyType extends string | number | symbol, ValueType> {private map: SimpleMap<KeyType, ValueType>;constructor() {this.map = {} as SimpleMap<KeyType, ValueType>;}set(key: KeyType, value: ValueType): void {this.map[key] = value;}get(key: KeyType): ValueType | undefined {return this.map[key];}delete(key: KeyType): boolean {const hasKey = key in this.map;if (hasKey) {delete this.map[key];}return hasKey;}has(key: KeyType): boolean {return key in this.map;}
}const myMap = new LightMap<string, number>();myMap.set('apple', 1);
myMap.set('banana', 2);console.log(myMap.get('apple')); // 输出:1
console.log(myMap.has('cherry')); // 输出:false
console.log(myMap.delete('banana')); // 输出:true
console.log(myMap.has('banana')); // 输出:false
- Record是TypeScript的一个内置类型别名,它允许你基于一个索引类型(键)和一个值类型创建一个对象类型
- 例如,Record<string, number>定义了一个所有键为字符串、值为数字的对象类型
- 我们的轻量级Map实现将包括以下几个基本操作:
- 设置值:
set(key, value)
- 获取值:
get(key)
- 删除值:
delete(key)
- 检查找键是否存在:
has(key)
- 设置值:
- 泛型SimpleMap,允许键为string、number或symbol,值为任意类型ValueType
- 通过TypeScript的Record类型和类,我们实现了一个轻量级的Map数据结构,它在编译时提供类型安全检查,确保了键值的类型正确性
- 虽然它不具备JavaScript内置Map对象的所有功能(如迭代器、容量自动扩展等),但足以应对一些简单场景,且在类型安全方面提供了额外保障
相关文章:

Typescript高级: 深入实践Record类型
概述 Record 类型是TS中其众多强大特性之一它为我们提供了创建键值对映射的强大能力极大地增强了代码的灵活性与类型安全性 应用示例 1 )用于配置场景 在复杂的项目中,配置文件往往包含多个模块的不同设置使用 Record 可以确保配置的键名正确且值类型…...

重构与优化-对象间特性搬移重构(2)
在软件开发过程中,重构是改进代码结构和设计、不改变其外在行为的过程。对象之间的特性搬移(Moving Features Between Objects)是重构的一种重要类型,它涉及到将属性、方法或其他特性从一个对象转移到另一个对象,以优化代码结构、提高可维护性和遵循设计原则。以下是几种典…...

网络流量监控与DNS流量分析
目录 一、网络流量监控的基础知识 什么是网络流量监控? 网络流量监控的重要性 实用案例:如何通过网络流量监控优化带宽利用 二、DNS流量分析的核心要点 什么是DNS流量分析? DNS流量分析的优势 实用技巧:如何通过DNS流量分…...

【数据分析】打造完美数据分析环境:Python开发环境搭建全攻略
打造完美数据分析环境:Python开发环境搭建全攻略 在数据分析的世界中,搭建一个稳定且高效的Python开发环境是至关重要的。本文将介绍三种主要的环境搭建方式:使用pip、Anaconda和Miniconda。 1. 使用pip从清华镜像安装Python包 pip是Pytho…...

我的app开始养活我了
大家在日常使用各类 app 时应该会发现,进入 app 会有个开屏广告,在使用 app 中,时不时的也会有广告被我们刷到。 这时候如果我们看完了这个广告,或者点击了这个广告的话,app商家就会获得这个广告的佣金。 这个佣金就是…...

linux中最基础使用的命令
小白学习记录: 前情提要:Linux命令基础格式!查看 ls看目录的小技巧 进入指定目录 cd查看当前工作目录 pwd创建一个新的目录(文件夹) mkdir创建文件 touch查看文件内容 cat、more操作文件、文件夹- 复制 cp- 移动 mv- 删除【危险操作ÿ…...

【算法实战】每日一题:17.1 订单处理问题(差分思想,二分搜索)
题目 一个会议中心的场地预订系统。在接下来的 n 天里,会议中心有一定数量的会议室可供租用。共有 m 份预订请求,每份请求描述为 (d_i, a_i, b_i),表示需要从第 a_i 天到第 b_i 天使用会议室(包括第 a_i 天和第 b_i 天࿰…...

UML静态图-对象图
概述 静态图包含类图、对象图和包图的主要目的是在系统详细设计阶段,帮助系统设计人员以一种可视化的方式来理解系统的内部结构和代码结构,包括类的细节、类的属性和操作、类的依赖关系和调用关系、类的包和包的依赖关系。 对象图与类图之间的关系&…...

数据结构第三篇【链表的相关知识点一及在线OJ习题】
数据结构第三篇【链表的相关知识点一及在线OJ习题】 链表链表的实现链表OJ习题顺序表和链表的区别和联系 本文章主要讲解关于链表的相关知识,喜欢的可以三连喔 😀😃😄😄😊😊🙃&#…...

RabbitMQ-发布/订阅模式
RabbitMQ-默认读、写方式介绍 RabbitMQ-直连交换机(direct)使用方法 目录 1、发布/订阅模式介绍 2、交换机(exchange) 3、fanout交换机的使用方式 3.1 声明交换机 3.2 发送消息到交换机 3.2 扇形交换机发送消息代码 3.2 声明队列,用于接收消息 3.3 binding …...

客运提质增效新模式!苏州金龙客货邮融合公交闪耀2024道路运输展
5月31日,“2024北京国际商用车及零部件展览会”暨“2024北京国际道路客货运输车辆及零部件展览会”(简称为“2024道路运输车辆展”)在中国国际展览中心(顺义馆)落下帷幕。本届展会以“智能、绿色、安全,助力…...

【Python实战】使用postman测试flask api接口
cookie_demo.py # -*- coding: utf-8 -*- """ Time : 2024/5/28 17:14 Author : 娜年花开 File : cookie_demo.py Desc : 需求:用户需要先登陆,登陆之后,通过Cookie来判断是不是能够访问登录后的接口userinfo &quo…...

Docker大学生看了都会系列(二、Mac通过Homebrew安装Docker)
系列文章目录 第一章 Docker介绍 第二章 Mac通过Homebrew安装Docker 文章目录 前言Mac通过Homebrew安装本机环境系统要求terminal命令安装查看安装信息配置阿里云镜像加速登陆阿里云配置加速地址其他国内加速地址 总结 前言 在上一章了解了Docker容器是什么之后,本…...

探索 Android Studio 中的 Gemini:加速 Android 开发的新助力
探索 Android Studio 中的 Gemini:加速 Android 开发的新助力 在 Gemini 时代的下一篇章中,Gemini融入了更多产品中,Android Studio 正在使用 Gemini 1.0 Pro 模型,使 Android 开发变得更快、更简单。 Studio Bot 现已更名为 And…...

linux运维——查看网卡实时流量脚本
方法一 以使用iftop命令来查看Linux系统中网卡的实时流量。如果您的系统还没有安装iftop,可以通过包管理器进行安装。 对于基于centos,可以使用以下命令安装: sudo yum install iftop 安装完成后,运行iftop命令查看实时流量&a…...

【三维重建NeRF(三)】Mip-NeRF论文解读
本文结合深蓝学院课程学习和本人的理解,欢迎交流指正 文章目录 Mip-NeRF流程简述混叠问题与MipMapMip-NeRF提出的解决办法圆锥台近似计算与集成位置编码(IPE) Mip-NeRF流程简述 Mip-NeRF的大体流程和NeRF基本是一样的,NeRF介绍 创新的部分就是针对NeRF…...

安卓SystemServer进程详解
目录 一、概述二、源码分析2.1 SystemServer fork流程分析2.1.1 [ZygoteInit.java] main()2.1.2 [ZygoteInit.java] forkSystemServer()2.1.3 [Zygote.java] forkSystemServer()2.1.4 [com_android_internal_os_Zygote.cpp]2.1.5 [com_android_internal_os_Zygote.cpp] ForkCom…...

Android studio 连接 adb传输文件到电脑
前提是已经连接到adb window R: 打开控制台adb devices:可以查看已经连接的设备adb pull /storage/emulated/0/Download/aa.png C:\Users\Administrator\Desktop:拉取连接设备的文件 aa.png 到电脑桌面上 (在电脑控制台进行拉取操作) 如果…...

Web学习篇(二)
命令执行漏洞 一、常用的函数 1、eval() 例: eval(string $code) 把字符串code作为PHP代码执行 2、assert() assert( mixed $assertion [, string $description ]) 检查一个断言是否为 FALSE,如果 assertion 是字符串,它将会被 assert()当做 PHP 代码来执行。 3、p…...

在Linux/Ubuntu/Debian系统中使用 `tar` 压缩文件
在Linux/Ubuntu/Debian系统中使用 tar 压缩文件 tar 命令是用于在类 Unix 操作系统中创建文件和目录存档的强大实用程序。 基本存档创建 要创建文件夹的简单存档,请使用以下命令: tar -cf ./my-archive.tar ./my-folder/此命令将创建一个名为 my-arc…...

Idea-Linux远程开发部署
第一步:File->Remote Development 第二步: 第三步: 第四步:在Host位置填写Linux虚拟机的IP地址,在Username、Password填写对应的账号密码后点击Test Connection测试连接。 第五步: 第六步:在…...

智能硬件会是下一个风口行业吗?
“风口行业”一直是人们热捧的择业目标,曾经红极一时房地产行业,此时已成沉舟侧畔之势,也意味着一个又一个行业时代的更迭。 随着5G时代的到来,“智能化”成了人们热议的话题,因为大家都懂:顺势而为才是王…...

mysql like 查询优化
1.如果我们查询的时候用like 模糊查询%a%,数据量大了会查询全局,效率很低 SELECT * FROM Customers WHERE CustomerName LIKE %a%; 优化: 不会破坏索引 -步骤-:创建适合Like查询的索引ALTER TABLE users ADD INDEX idx_username (usernam…...

3389连接器,3389连接器如何进行安全设置
在计算机网络领域,3389端口作为Windows系统默认的远程桌面协议(RDP)端口,在远程办公、技术支持等场景中发挥着重要作用。然而,由于其广泛的使用和直接暴露在互联网上的特性,3389端口也极易成为黑客攻击的目…...

代码随想录训练营Day56:Leetcode647、516
Leetcode647: 问题描述: 给你一个字符串 s ,请你统计并返回这个字符串中 回文子串 的数目。 回文字符串 是正着读和倒过来读一样的字符串。 子字符串 是字符串中的由连续字符组成的一个序列。 示例 1: 输入:s &q…...

LLM主要类别架构
LLM主要类别架构介绍 LLM主要类别 LLM本身基于transformer架构。自2017年,attention is all you need诞生起,transformer模型为不同领域的模型提供了灵感和启发。基于原始的Transformer框架,衍生出了一系列模型,一些模型仅仅使用e…...

试比较GD32E230系列与L233/235芯片在IIC上使用温度传感器SHT40的异同
不说废话,上代码,不同之处直接用宏 展开 1. 首先是i2c 时钟配置 函数有些出入 void sensirion_i2c_attribute_config(){#ifdef GD32E230/* I2C clock configure */i2c_clock_config(I2C1, 100000, I2C_DTCY_2);/* I2C address configure */i2c_mode_a…...

超强算力 Orange Pi Kunpeng Pro 开发板基础测评与体验
目录 开箱体验资源简介系统启动连接网络登录系统通过桌面登录通过串口登录通过 SSH 登录配置散热风扇 算力测试MNIST示例MBNET示例 体验总结 大家好,我是 Hello 阿尔法,有幸接到 CSDN 的邀请参与 Orange Pi Kunpeng Pro 开发板的测评活动,本文…...

vs - ms官方查看pdb文件内容的例子工程
文章目录 vs - ms官方查看pdb文件内容的例子工程概述笔记END vs - ms官方查看pdb文件内容的例子工程 概述 不管哪个版本的VS,生成debug版工程时,除了工程自己的.pdb还会有vc*.pdb. e.g. vc.pdb, vc70.pdb, vc90.pdb, vc120.pdb, vc140.pdb, vc142.pdb,…...

【excel】设置二级可变联动菜单
文章目录 【需求】在一级菜单选定后,二级菜单联动显示一级菜单下的可选项【步骤】step1 制作辅助列1.列转行2.在辅助列中匹配班级成员 step2 名称管理器step3 制作二级下拉菜单step4 消除二级菜单中的空白 【总结】 之前做完了 【excel】设置可变下拉菜单ÿ…...