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

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 &#xff09;用于配置场景 在复杂的项目中&#xff0c;配置文件往往包含多个模块的不同设置使用 Record 可以确保配置的键名正确且值类型…...

重构与优化-对象间特性搬移重构(2)

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

网络流量监控与DNS流量分析

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

【数据分析】打造完美数据分析环境:Python开发环境搭建全攻略

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

我的app开始养活我了

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

linux中最基础使用的命令

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

【算法实战】每日一题:17.1 订单处理问题(差分思想,二分搜索)

题目 一个会议中心的场地预订系统。在接下来的 n 天里&#xff0c;会议中心有一定数量的会议室可供租用。共有 m 份预订请求&#xff0c;每份请求描述为 (d_i, a_i, b_i)&#xff0c;表示需要从第 a_i 天到第 b_i 天使用会议室&#xff08;包括第 a_i 天和第 b_i 天&#xff0…...

UML静态图-对象图

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

数据结构第三篇【链表的相关知识点一及在线OJ习题】

数据结构第三篇【链表的相关知识点一及在线OJ习题】 链表链表的实现链表OJ习题顺序表和链表的区别和联系 本文章主要讲解关于链表的相关知识&#xff0c;喜欢的可以三连喔 &#x1f600;&#x1f603;&#x1f604;&#x1f604;&#x1f60a;&#x1f60a;&#x1f643;&#…...

RabbitMQ-发布/订阅模式

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

客运提质增效新模式!苏州金龙客货邮融合公交闪耀2024道路运输展

5月31日&#xff0c;“2024北京国际商用车及零部件展览会”暨“2024北京国际道路客货运输车辆及零部件展览会”&#xff08;简称为“2024道路运输车辆展”&#xff09;在中国国际展览中心&#xff08;顺义馆&#xff09;落下帷幕。本届展会以“智能、绿色、安全&#xff0c;助力…...

【Python实战】使用postman测试flask api接口

cookie_demo.py # -*- coding: utf-8 -*- """ Time : 2024/5/28 17:14 Author : 娜年花开 File : cookie_demo.py Desc : 需求&#xff1a;用户需要先登陆&#xff0c;登陆之后&#xff0c;通过Cookie来判断是不是能够访问登录后的接口userinfo &quo…...

Docker大学生看了都会系列(二、Mac通过Homebrew安装Docker)

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

探索 Android Studio 中的 Gemini:加速 Android 开发的新助力

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

linux运维——查看网卡实时流量脚本

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

【三维重建NeRF(三)】Mip-NeRF论文解读

本文结合深蓝学院课程学习和本人的理解&#xff0c;欢迎交流指正 文章目录 Mip-NeRF流程简述混叠问题与MipMapMip-NeRF提出的解决办法圆锥台近似计算与集成位置编码(IPE) Mip-NeRF流程简述 Mip-NeRF的大体流程和NeRF基本是一样的&#xff0c;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&#xff1a; 打开控制台adb devices&#xff1a;可以查看已经连接的设备adb pull /storage/emulated/0/Download/aa.png C:\Users\Administrator\Desktop&#xff1a;拉取连接设备的文件 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 操作系统中创建文件和目录存档的强大实用程序。 基本存档创建 要创建文件夹的简单存档&#xff0c;请使用以下命令&#xff1a; tar -cf ./my-archive.tar ./my-folder/此命令将创建一个名为 my-arc…...

51c自动驾驶~合集58

我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留&#xff0c;CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制&#xff08;CCA-Attention&#xff09;&#xff0c;…...

MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例

一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...

系统设计 --- MongoDB亿级数据查询优化策略

系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log&#xff0c;共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题&#xff0c;不能使用ELK只能使用…...

Golang dig框架与GraphQL的完美结合

将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用&#xff0c;可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器&#xff0c;能够帮助开发者更好地管理复杂的依赖关系&#xff0c;而 GraphQL 则是一种用于 API 的查询语言&#xff0c;能够提…...

1.3 VSCode安装与环境配置

进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件&#xff0c;然后打开终端&#xff0c;进入下载文件夹&#xff0c;键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...

mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包

文章目录 现象&#xff1a;mysql已经安装&#xff0c;但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时&#xff0c;可能是因为以下几个原因&#xff1a;1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

音视频——I2S 协议详解

I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议&#xff0c;专门用于在数字音频设备之间传输数字音频数据。它由飞利浦&#xff08;Philips&#xff09;公司开发&#xff0c;以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...

提升移动端网页调试效率:WebDebugX 与常见工具组合实践

在日常移动端开发中&#xff0c;网页调试始终是一个高频但又极具挑战的环节。尤其在面对 iOS 与 Android 的混合技术栈、各种设备差异化行为时&#xff0c;开发者迫切需要一套高效、可靠且跨平台的调试方案。过去&#xff0c;我们或多或少使用过 Chrome DevTools、Remote Debug…...

LangChain 中的文档加载器(Loader)与文本切分器(Splitter)详解《二》

&#x1f9e0; LangChain 中 TextSplitter 的使用详解&#xff1a;从基础到进阶&#xff08;附代码&#xff09; 一、前言 在处理大规模文本数据时&#xff0c;特别是在构建知识库或进行大模型训练与推理时&#xff0c;文本切分&#xff08;Text Splitting&#xff09; 是一个…...