当前位置: 首页 > 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…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…...

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…...

python打卡day49

知识点回顾&#xff1a; 通道注意力模块复习空间注意力模块CBAM的定义 作业&#xff1a;尝试对今天的模型检查参数数目&#xff0c;并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...

Spring Boot 实现流式响应(兼容 2.7.x)

在实际开发中&#xff0c;我们可能会遇到一些流式数据处理的场景&#xff0c;比如接收来自上游接口的 Server-Sent Events&#xff08;SSE&#xff09; 或 流式 JSON 内容&#xff0c;并将其原样中转给前端页面或客户端。这种情况下&#xff0c;传统的 RestTemplate 缓存机制会…...

无法与IP建立连接,未能下载VSCode服务器

如题&#xff0c;在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈&#xff0c;发现是VSCode版本自动更新惹的祸&#xff01;&#xff01;&#xff01; 在VSCode的帮助->关于这里发现前几天VSCode自动更新了&#xff0c;我的版本号变成了1.100.3 才导致了远程连接出…...

大数据零基础学习day1之环境准备和大数据初步理解

学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 &#xff08;1&#xff09;设置网关 打开VMware虚拟机&#xff0c;点击编辑…...

dedecms 织梦自定义表单留言增加ajax验证码功能

增加ajax功能模块&#xff0c;用户不点击提交按钮&#xff0c;只要输入框失去焦点&#xff0c;就会提前提示验证码是否正确。 一&#xff0c;模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...

Frozen-Flask :将 Flask 应用“冻结”为静态文件

Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是&#xff1a;将一个 Flask Web 应用生成成纯静态 HTML 文件&#xff0c;从而可以部署到静态网站托管服务上&#xff0c;如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互

引擎版本&#xff1a; 3.8.1 语言&#xff1a; JavaScript/TypeScript、C、Java 环境&#xff1a;Window 参考&#xff1a;Java原生反射机制 您好&#xff0c;我是鹤九日&#xff01; 回顾 在上篇文章中&#xff1a;CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)

设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile&#xff0c;新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...