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

阮一峰《TypeScript 教程》学习笔记——类型映射

1. 一段话总结

TypeScript 的类型映射是将一种类型按规则转换为另一种类型的核心机制,主要用于对象类型,核心语法为 [prop in keyof Type]: TargetType(通过keyof提取原类型键名,in遍历键名联合类型);支持通过映射修饰符+?/-?控制可选属性、+readonly/-readonly控制只读属性)调整属性的可选/只读特性;TypeScript 4.1 引入的键名重映射as子句,如[p in keyof A as ${p}ID])可修改键名,还能实现属性过滤(通过条件类型返回never排除属性);此外,类型映射可封装为泛型提高复用性,且与Partial<T>/Readonly<T>/Required<T>等内置工具类型直接关联,广泛用于属性类型批量修改、特性调整及键名优化。


2. 思维导图

在这里插入图片描述


3. 详细总结

一、类型映射简介

TypeScript 类型映射的核心是“按预设规则将源类型转换为目标类型”,主要针对对象类型,解决批量处理属性类型或特性的问题,避免重复编写相似类型。

1. 基础语法与核心逻辑

语法组成作用示例
prop属性名变量,名称可自定义(如PKey-
in遍历运算符,逐个取出右侧联合类型的成员P in keyof A(遍历A的键名)
keyof Type提取源类型Type的所有键名,生成联合类型(如keyof {a:number} → 'a'`keyof {foo:number; bar:string} → ‘foo’
TargetType目标属性类型,可固定(如string)或关联源类型(如Type[P][P in keyof A]: A[P](复制源类型)

示例1:基础类型转换
将源类型A的所有属性转为string类型:

type A = { foo: number; bar: number };
type B = { [P in keyof A]: string }; // { foo: string; bar: string }

示例2:复制源类型
通过Type[P]关联源属性类型,实现类型复制:

type A = { foo: number; bar: string };
type B = { [P in keyof A]: A[P] }; // 与A完全一致

2. 泛型封装(提高复用性)

将映射逻辑封装为泛型,可复用至任意对象类型:

// 泛型:将任意类型的所有属性转为boolean
type ToBoolean<Type> = { [P in keyof Type]: boolean };
// 用法:将User的所有属性转为boolean
type User = { name: string; age: number };
type UserBoolean = ToBoolean<User>; // { name: boolean; age: boolean }

二、映射修饰符

映射会默认保留源类型的“可选(?)”和“只读(readonly)”特性,若需调整,需使用映射修饰符+添加特性,-移除特性)。

1. 修饰符分类与用法

修饰符类型语法作用示例(基于type A = { a: string; b: number }
添加可选属性+?(简写为?为所有属性添加?,转为可选属性type OptionalA = { [P in keyof A]?: A[P] } → { a?: string; b?: number }
移除可选属性-?移除所有属性的?,转为必选属性(对应内置Required<T>type ConcreteA = { [P in keyof A]-?: A[P] } → { a: string; b: number }
添加只读属性+readonly(简写为readonly为所有属性添加readonly,转为只读属性(对应内置Readonly<T>type ReadonlyA = { readonly [P in keyof A]: A[P] } → { readonly a: string; readonly b: number }
移除只读属性-readonly移除所有属性的readonly,转为可写属性type MutableA = { -readonly [P in keyof A]: A[P] } → { a: string; b: number }

2. 组合使用修饰符

同时调整“只读”和“可选”特性:

// 为所有属性添加readonly和可选
type ReadonlyOptional<T> = { +readonly [P in keyof T]+?: T[P] };
// 移除所有属性的readonly和可选
type MutableConcrete<T> = { -readonly [P in keyof T]-?: T[P] };

三、键名重映射(TypeScript 4.1+)

键名重映射通过as子句修改源类型的键名,支持模板字符串、条件类型等,还可实现属性过滤。

1. 核心语法

type 目标类型 = {[P in keyof 源类型 as 新键名类型]: 目标属性类型;
};
  • 新键名类型:常用模板字符串(如${P}ID)、条件类型(如T[P] extends string ? P : never)。

2. 常见场景示例

场景代码示例结果类型
键名添加后缀type A = { foo: number; bar: number }; type B = { [P in keyof A as ${P}ID]: number }{ fooID: number; barID: number }
键名添加前缀并大写type Person = { name: string; age: number }; type Getters<T> = { [P in keyof T as get${Capitalize<string & P>}]: () => T[P] }{ getName: ()=>string; getAge: ()=>number }
属性过滤(保留字符串属性)type User = { name: string; age: number }; type FilterString<T> = { [K in keyof T as T[K] extends string ? K : never]: string }{ name: string }

3. 关键逻辑:属性过滤

通过条件类型,若属性不满足要求则返回never(表示该属性不存在),实现过滤:

type FilterNumber<T> = {// 仅保留属性值为number的属性,否则键名转为never(过滤)[K in keyof T as T[K] extends number ? K : never]: number;
};
type Data = { a: number; b: string; c: number };
type NumberData = FilterNumber<Data>; // { a: number; c: number }

四、联合类型的映射

键名重映射支持基于“非键名联合类型”(如对象联合类型)生成新类型,核心是从联合类型成员中提取字段作为新键名。

示例:基于含kind字段的对象联合类型生成函数类型对象

// 源联合类型:含kind字段的对象类型
type Square = { kind: 'square'; x: number; y: number };
type Circle = { kind: 'circle'; radius: number };// 映射逻辑:以kind字段值为键名,生成函数类型
type EventHandlers<Events extends { kind: string }> = {[E in Events as E['kind']]: (event: E) => void;
};// 生成目标类型
type ShapeHandlers = EventHandlers<Square | Circle>;
// 结果:{ square: (event:Square)=>void; circle: (event:Circle)=>void }

五、与内置工具类型的关联

TypeScript 内置工具类型多基于类型映射实现,核心对应关系如下:

内置工具类型实现逻辑(简化版)作用
Partial<T>{ [P in keyof T]?: T[P] }将所有属性转为可选
Readonly<T>{ readonly [P in keyof T]: T[P] }将所有属性转为只读
Required<T>{ [P in keyof T]-?: T[P] }移除所有属性的可选特性,转为必选

4. 关键问题

问题1:TypeScript 类型映射的基础语法是什么?其核心作用的解决了什么开发痛点?

答案

  • 基础语法:类型映射的核心语法为 [prop in keyof Type]: TargetType,其中:
    • keyof Type:提取源类型Type的所有键名,生成联合类型;
    • in:遍历该联合类型,逐个获取键名并赋值给变量prop
    • TargetType:指定映射后属性的目标类型(可固定或关联源类型,如Type[prop])。
  • 解决的痛点
    开发中若需批量处理对象类型的属性(如将所有属性转为boolean、统一添加readonly),手动编写重复类型定义会导致冗余且易出错;类型映射通过“规则化转换”实现批量处理,减少重复代码,同时提高类型定义的复用性(如封装为泛型后可复用至任意对象类型)。
  • 示例
    批量将User类型的所有属性转为string
    type User = { name: string; age: number };
    type UserString = { [P in keyof User]: string }; // { name: string; age: string }
    

问题2:映射修饰符有哪几类?分别适用于哪些场景?请结合示例说明。

答案
映射修饰符分为可选属性修饰符只读属性修饰符,核心用于调整属性的“可选”或“只读”特性,共4种常用类型:

修饰符类别具体修饰符语法示例适用场景
可选属性修饰符添加可选[P in keyof T]+?: T[P](简写?需将对象所有属性转为可选(如表单初始值类型)
可选属性修饰符移除可选[P in keyof T]-?: T[P]需将对象所有可选属性转为必选(如校验后的数据类型)
只读属性修饰符添加只读+readonly [P in keyof T]: T[P](简写readonly需禁止修改对象属性(如配置项类型)
只读属性修饰符移除只读-readonly [P in keyof T]: T[P]需解除属性的只读限制(如动态更新的数据类型)

示例1:添加可选属性
Config类型的所有属性转为可选,用于表单初始值:

type Config = { theme: string; fontSize: number };
type OptionalConfig = { [P in keyof Config]?: Config[P] };
const initConfig: OptionalConfig = { theme: 'light' }; // 允许省略fontSize

示例2:移除可选属性
OptionalConfig的可选属性转为必选,用于校验后的确定数据:

type RequiredConfig = { [P in keyof OptionalConfig]-?: OptionalConfig[P] };
const finalConfig: RequiredConfig = { theme: 'light', fontSize: 16 }; // 必须包含所有属性

问题3:键名重映射的语法是什么?如何通过它实现“属性过滤”?请结合示例说明原理。

答案

  • 键名重映射语法:TypeScript 4.1+ 支持通过as子句修改键名,语法为:

    type 目标类型 = { [P in keyof 源类型 as 新键名类型]: 目标属性类型 };
    

    其中“新键名类型”可通过模板字符串(如${P}ID)、条件类型等生成,核心是通过as子句重新定义键名。

  • 属性过滤原理
    利用“条件类型返回never表示属性不存在”的特性,在as子句中添加条件判断:若属性满足要求,则保留原键名;若不满足,则返回never(过滤该属性)。

  • 示例:过滤非函数类型的属性
    Action类型中仅保留“属性值为函数”的属性:

    // 源类型:含函数和非函数属性
    type Action = {add: (x: number, y: number) => number;name: string;subtract: (x: number, y: number) => number;age: number;
    };// 映射逻辑:仅保留函数类型属性
    type FilterFunction<T> = {[K in keyof T as T[K] extends (...args: any[]) => any ? K : never]: T[K];
    };// 过滤结果:仅保留add和subtract
    type FunctionAction = FilterFunction<Action>;
    // 结果类型:{ add: (x:number,y:number)=>number; subtract: (x:number,y:number)=>number }
    

    原理:对每个键名K,判断T[K]是否为函数类型;若是,保留键名K;若不是,返回never(该属性被过滤)。

http://www.dtcms.com/a/532067.html

相关文章:

  • 需要做网站建设和推广网站地图插件
  • PyCharm 设置 Tabs and Indents
  • Spring Boot3零基础教程,生命周期监听,自定义监听器,笔记59
  • 【SpringBoot】详解Maven的操作与配置
  • 【C++】STL容器--priority_queue的使用与模拟实现
  • 【系统分析师】高分论文:论需求分析方法及应用(电子商务门户网站系统)
  • 【大模型应用开发 6.LangChain多任务应用开发】
  • 泰安最好的网站建设公司怎么通过做网站赚钱吗
  • 初识C语言15.文件操作
  • 聊聊连续、递增
  • 9款上班打卡软件测评:帮你选出最适合企业的工具
  • 建站服务网络公司建设宣传网站上的请示
  • Mem0 使用案例学习总结 - 记忆化应用结构
  • 如何自动清理 Linux 临时文件 ?
  • C++容器forward_list
  • 茂名网站建设培训品牌宣传网站
  • 油漆工找活做的网站网站免费建设
  • TcpClinet
  • Appium+Python+Android+Nodejs环境安装
  • SCDN:互联网时代网站安全的安全保障
  • Linux小课堂: Apache服务在CentOS上的安装与基础配置指南
  • Gorm(八)预加载方式
  • 网站开发与设计的实训场地WordPress无法自动推送
  • 【找指针数组最大值】2022-11-24
  • 自己做网站能赚钱吗做spa的网站怎么推广
  • 网络管理中的名词
  • gitlab配置git的ssh秘钥
  • 机器狗进化论:当“园区跑腿”遇上具身智能,一场静悄悄的变革正在发生
  • 江宁区建设工程质量监督站网站学校网站源码开源
  • Docker LXC深度解析:从基础概念到实战演练