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

TypeScript编程06-映射类型

TypeScript编程06-映射类型

文章目录

  • TypeScript编程06-映射类型
    • 第一章:基础概念
      • 1.1 什么是映射类型?
    • 第二章:Partial<T> - 可选化
      • 2.1 基本用法
      • 2.2 实现原理
    • 第三章:Required<T> - 必選化
      • 3.1 基本用法
      • 3.2 实现原理
    • 第四章:Readonly<T> - 只读化
      • 4.1 基本用法
      • 4.2 浅只读 vs 深只读
    • 第五章:Pick<T, K> - 属性挑选
      • 5.1 基本用法
      • 5.2 实现原理
    • 第六章:Omit<T, K> - 属性排除
      • 6.1 基本用法
      • 6.2 与 Pick 的关系
    • 第七章:Record<K, T> - 键值映射
      • 7.1 基本用法
      • 7.2 与索引签名的区别
    • 第八章:组合应用与实战
      • 8.1 如何组合使用这些映射类型?
      • 8.2 自定义映射类型
    • 第九章:常见问题与陷阱
      • 9.1 可选属性与 undefined 的区别
      • 9.2 映射类型对方法的影响
      • 9.3 keyof 与联合类型的顺序
    • 第十章:速查表

第一章:基础概念

1.1 什么是映射类型?

Q:什么是 TypeScript 中的映射类型?

A:映射类型(Mapped Types)是 TypeScript 提供的基于旧类型创建新类型的工具,它会遍历目标类型的每个属性并应用特定的转换规则。本质上是一种类型层面的"批量操作"工具。

// 基础语法结构typeNewType={[KinkeyofOldType]:NewValueType}

第二章:Partial<T> - 可选化

2.1 基本用法

Q:Partial<T>的作用是什么?

A:将类型T的所有属性变为可选(?)。常用于函数参数、表单更新等场景。

interfaceUser{name:string;age:number;email:string;}typePartialUser=Partial<User>;// 等价于:// {// name?: string;// age?: number;// email?: string;// }// 实际应用:更新用户信息functionupdateUser(user:User,updates:Partial<User>):User{return{...user,...updates};}constuser={name:"张三",age:25,email:"zhang@example.com"};updateUser(user,{age:26});// ✅ 只需传入想更新的字段

2.2 实现原理

Q:Partial<T>是如何实现的?

A:

typePartial<T>={[PinkeyofT]?:T[P];};

关键点:

  • keyof T:获取T的所有键组成的联合类型
  • P in ...:遍历每个键
  • ?::将属性标记为可选

第三章:Required<T> - 必選化

3.1 基本用法

Q:Required<T>的作用是什么?

A:将类型T的所有属性变为必选(移除?)。与Partial<T>作用相反。

interfaceConfig{host?:string;port?:number;ssl?:boolean;}typeRequiredConfig=Required<Config>;// 等价于:// {// host: string;// port: number;// ssl: boolean;// }// 实际应用:确保配置完整functioncreateServer(config:RequiredConfig){// 此时 config 的所有属性都一定有值console.log(config.host,config.port,config.ssl);}

3.2 实现原理

Q:Required<T>是如何实现的?

A:

typeRequired<T>={[PinkeyofT]-?:T[P];};

关键点:

  • -?:移除可选修饰符(-表示移除,?表示可选)

第四章:Readonly<T> - 只读化

4.1 基本用法

Q:Readonly<T>的作用是什么?

A:将类型T的所有属性变为只读(readonly),防止属性被重新赋值。

interfacePoint{x:number;y:number;}typeReadonlyPoint=Readonly<Point>;// 等价于:// {// readonly x: number;// readonly y: number;// }constpoint:ReadonlyPoint={x:10,y:20};point.x=5;// ❌ 报错:无法为只读属性赋值

4.2 浅只读 vs 深只读

Q:Readonly<T>是深只读吗?

A:不是,它是浅只读(Shallow Readonly),只影响第一层属性。

interfaceUser{info:{name:string;age:number};}constuser:Readonly<User>={info:{name:"张三",age:25}};// user.info = { name: "李四", age: 30 }; // ❌ 报错:不能修改 info 本身user.info.name="李四";// ✅ 不报错:可以修改嵌套对象的属性// 深只读实现typeDeepReadonly<T>={readonly[PinkeyofT]:T[P]extendsobject?DeepReadonly<T[P]>:T[P];};constdeepUser:DeepReadonly<User>={info:{name:"张三",age:25}};// deepUser.info.name = "李四"; // ❌ 报错:深层属性也是只读的

第五章:Pick<T, K> - 属性挑选

5.1 基本用法

Q:Pick<T, K>的作用是什么?

A:从类型T中挑选出一组属性K来构造新类型。

interfaceUser{id:number;name:string;email:string;password:string;createdAt:Date;}// 只选择 id 和 nametypeUserPreview=Pick<User,"id"|"name">;// 等价于:// {// id: number;// name: string;// }// 实际应用:API 返回精简数据functiongetUserPreview(id:number):Promise<UserPreview>{returnfetch(`/api/users/${id}/preview`).then(r=>r.json());}

5.2 实现原理

Q:Pick<T, K>是如何实现的?

A:

typePick<T,KextendskeyofT>={[PinK]:T[P];};

关键点:

  • K extends keyof T:约束K必须是T的键的子集
  • 遍历K而非keyof T,实现选择性提取

第六章:Omit<T, K> - 属性排除

6.1 基本用法

Q:Omit<T, K>的作用是什么?

A:从类型T中排除一组属性K来构造新类型。与Pick<T, K>相反。

interfaceUser{id:number;name:string;email:string;password:string;createdAt:Date;}// 排除敏感字段 password 和 createdAttypePublicUser=Omit<User,"password"|"createdAt">;// 等价于:// {// id: number;// name: string;// email: string;// }// 实际应用:API 返回公开数据functionsanitizeUser(user:User):PublicUser{const{password,createdAt,...publicInfo}=user;returnpublicInfo;}

6.2 与 Pick 的关系

Q:OmitPick可以互相转换吗?

A:可以,它们是互补的:

// Omit 可以用 Pick + Exclude 实现typeOmit<T,Kextendskeyofany>=Pick<T,Exclude<keyofT,K>>;// 示例验证typeA=Omit<User,"password">;typeB=Pick<User,"id"|"name"|"email"|"createdAt">;// A 和 B 是等价的

第七章:Record<K, T> - 键值映射

7.1 基本用法

Q:Record<K, T>的作用是什么?

A:构造一个对象类型,其键的类型为K,值的类型为T。常用于创建字典、映射表。

// 键为字符串,值为数字typePageNames="home"|"about"|"contact";typePageInfo=Record<PageNames,{title:string;path:string}>;constpages:PageInfo={home:{title:"首页",path:"/"},about:{title:"关于",path:"/about"},contact:{title:"联系",path:"/contact"}// 不能添加其他键,也不能缺少任何键};

7.2 与索引签名的区别

Q:Record<K, T>和索引签名有什么区别?

A:

特性Record<K, T>索引签名
键的范围有限的联合类型无限的类型(如string
必须包含所有键✅ 是❌ 否
适用场景固定键集合动态键集合
// Record:键是有限的typeStatus="pending"|"success"|"error";conststatusMap:Record<Status,string>={pending:"处理中",success:"成功",error:"失败"// 不能少也不能多};// 索引签名:键是无限的interfaceDictionary{[key:string]:number;}constdict:Dictionary={a:1,b:2,// 可以有任意多个键};

第八章:组合应用与实战

8.1 如何组合使用这些映射类型?

Q:实际开发中如何组合使用这些工具类型?

A:可以链式组合,解决复杂类型问题:

interfaceProduct{id:number;name:string;price:number;stock:number;description:string;createdAt:Date;updatedAt:Date;}// 场景:创建产品时,只需要部分字段,且都是可选的typeCreateProductDTO=Partial<Pick<Product,"name"|"price"|"description"|"stock">>;// 场景:更新产品时,id 必选,其他可选typeUpdateProductDTO=Partial<Omit<Product,"id"|"createdAt"|"updatedAt">>&Pick<Product,"id">;// 场景:前端展示用的产品信息(排除敏感字段,且只读)typeProductView=Readonly<Omit<Product,"stock"|"updatedAt">>;// 场景:按状态分组的产品列表typeProductStatus="available"|"outOfStock"|"discontinued";typeProductsByStatus=Record<ProductStatus,Product[]>;

8.2 自定义映射类型

Q:如何基于这些工具类型创建自定义映射类型?

A:可以扩展或组合内置工具类型:

// 1. 可空类型(值可以是 null)typeNullable<T>={[PinkeyofT]:T[P]|null;};// 2. 可选且只读typeReadonlyPartial<T>=Readonly<Partial<T>>;// 3. 深部分(递归处理嵌套对象)typeDeepPartial<T>={[PinkeyofT]?:T[P]extendsobject?DeepPartial<T[P]>:T[P];};// 4. 提取函数参数类型typeParameters<Textends(...args:any[])=>any>=Textends(...args:inferP)=>any?P:never;// 5. 提取函数返回值类型typeReturnType<Textends(...args:any[])=>any>=Textends(...args:any[])=>inferR?R:any;

第九章:常见问题与陷阱

9.1 可选属性与 undefined 的区别

Q:Partial<T>和将属性设为undefined有什么区别?

A:

interfaceA{x?:string;// 可选属性:可以不存在y:string|undefined;// 必选但可为 undefined:必须存在,值可以是 undefined}consta1:A={};// ❌ 缺少 yconsta2:A={y:undefined};// ✅ y 必须存在,即使值是 undefined

9.2 映射类型对方法的影响

Q:Readonly<T>会影响类的方法吗?

A:会,但不会阻止方法内部修改状态:

classCounter{count=0;increment(){this.count++;// 内部可以修改}}constreadonlyCounter:Readonly<Counter>=newCounter();// readonlyCounter.count = 5; // ❌ 报错readonlyCounter.increment();// ✅ 允许调用,内部修改了 count

9.3 keyof 与联合类型的顺序

Q:PickOmit的第二个参数顺序重要吗?

A:不重要,联合类型是无序的:

typeA=Pick<User,"id"|"name">;typeB=Pick<User,"name"|"id">;// A 和 B 完全相同

第十章:速查表

工具类型作用记忆口诀
Partial<T>所有属性可选Partial =Partial(部分的)
Required<T>所有属性必选Required =Required(必需的)
Readonly<T>所有属性只读Readonly =Read(只读)
Pick<T, K>挑选指定属性Pick =Pick(挑选)
Omit<T, K>排除指定属性Omit =Omit(省略)
Record<K, T>创建键值映射Record =Record(记录/字典)

http://www.cnnetsun.cn/news/1684553.html

相关文章:

  • 开箱即用!fft npainting lama镜像部署与图像修复快速上手
  • PowerPaint-V1 Gradio快速上手:上传图片+涂抹区域,3分钟完成修图
  • Qwen3-0.6B-FP8快速部署:低功耗笔记本(MX550)实测运行可行性报告
  • Asian Beauty Z-Image Turbo保姆级教程:5分钟本地部署,一键生成东方美人图
  • 3步永久珍藏QQ空间青春记忆:GetQzonehistory完整备份指南
  • 快速上手Wan2.2-I2V-A14B:私有部署镜像详解,从环境到生成一步到位
  • 如何高效解锁《原神》帧率限制:完整技术指南与实战配置
  • [特殊字符] Godot Unpacker终极指南:3分钟掌握游戏资源提取的完整操作法
  • LumiPixel Canvas Quest心理疗愈应用:生成积极意象引导图
  • 保姆级教程:在Kali Linux上搞定AIC8800DC无线网卡,从驱动到抓包实战
  • WarcraftHelper:让经典《魔兽争霸III》无缝适配现代设备的终极工具
  • VideoAgentTrek Screen Filter与ChatGPT联动:智能生成视频过滤报告
  • 别再只盯着翻译了!聊聊杰理AI蓝牙耳机SDK还能玩出什么花样:语音备忘录、会议记录与智能提醒
  • 3大核心价值助力自媒体高效采集:抖音无水印下载工具全解析
  • 万象视界灵坛应用场景:短视频封面图语义一致性智能审核
  • 打破3D软件壁垒:MMD Tools让跨平台创作效率提升300%
  • 基于LSTM时间序列预测思想优化Qwen3对话连贯性
  • Python RPA(tagui)实战避坑 - 以咸鱼之王自动化挂机为例
  • 美胸-年美-造相Z-Turbo使用技巧:如何写出更好的提示词生成图片
  • Docker 容器化部署 qBittorrent WebUI 及内网穿透实战指南
  • 新手编剧福音:像素剧本圣殿开箱即用,免费生成高质量剧本初稿
  • M2LOrder 辅助 Java 八股文学习:智能分析面试题解析中的情绪倾向
  • SecGPT-14B技能扩展:为OpenClaw增加5个网络安全专用模块
  • 解决WebSocket协议问题,提升摄像头实时监控
  • 双通道并用:OpenClaw同时接入gemma-3-12b-it与本地知识库
  • StructBERT零样本分类案例展示:自定义标签分类效果惊艳
  • Wan2.1 VAE数据预处理实战:Python爬虫采集的训练数据清洗
  • 【2026年最新600套毕设项目分享】springboot政府集中采购管理系统(14317)
  • 英雄联盟身份重塑:5分钟掌握LeaguePrank的终极伪装指南
  • 零基础玩转Qwen3-VL-8B:手把手教你搭建自己的识图AI助手