相信现在很多小伙伴都在使用 TypeScript(以下简称 TS),在 TS 中除了一些常用的基本类型外,还有一些稍微高级一点的类型,这些就是我本次文章要讲的内容:索引类型与映射类型,希望小伙伴们看过这篇文章后能对 TS 有更深一步的理解。

索引类型

下面我通过一个官方的例子来说明下什么是索引类型:

function pluck(o, names) {  return names.map((n) => o[n])}

这是个简单的函数,names 是一个数组,里面是 key 值,我们可以从“o”里面取出这些 key 值,理想情况下 names 里面的 key 应该都是“o”里面包含的,否则最终的结果里面就会有 undefined,这个函数返回的结果也应该是“o”中都包含的 value 值,那么我们如何才能做到这些类型约束呢,如果只用一些基础类型,很难达到满意的效果,下面使用索引类型改写下:

function pluck<T, K extends keyof T>(o: T, names: K[]): T[K][] {  return names.map((n) => o[n])}interface Person {  name: string  age: number}let person: Person = {  name: 'Jarid',  age: 35}let strings: string[] = pluck(person, ['name']) // ok, string[]

改写后这个函数是一个泛型函数,泛型为 T 和 K,其中 K 有点特殊,K extends keyof T,是什么意思呢,其中 keyof 就是索引类型查询操作符,我们从字面意思理解,它就是 T 的 key,就是 T 上已知的公共属性名的联合,对于上面的代码,keyof Person就是'name'|'age',那么K extends keyof T就是K extends 'name'|'age',这样我们就获取到了 Person 上所有 key 组成的一个联合类型,然后参数o: T, names: K[],就很好理解了,names 就是 K 组成的一个数组。返回值中T[K][]我们需要拆开来看 T[K]和[],就是 T[K]组成的一个数组,那么 T[K]是什么类型呢,它就是索引访问操作符,类似于 js 中对象的取值操作,不过这里取的是类型,因为 K 是'name'|'age',所以 T[K]就是string|number,这些就是索引类型,其实也不难理解,下面再说下映射类型,它和索引类型结合起来可以做很多事情。

映射类型

映射类型也很容易理解,我们先看一个简单的例子

type Keys = 'option1' | 'option2'type Flags = { [K in Keys]: boolean }

这个就是一个简单的映射类型,其中的in可以理解为是我们平时用的for...in,就是去遍历 Keys,然后把 boolean 赋给每一个 key,上面的 Flags 得到的结果就是

type Flags = {  option1: boolean  option2: boolean}

很简单吧,那么这个东西有什么用处呢,请看下面的例子:

// Persontype Person {    name: string    age: number}

我们想把这个 Person 里面的属性都变成只读的,像这样:

// Readonly Persontype Person {    readonly name: string    readonly age: number}

如果我们有很多这样的类型,那么改起来会很麻烦,因为每次都要把这个类型重新写一遍。其实我们可以使用刚才的索引类型和映射类型来写一个泛型:

type Readonly = {  readonly [P in keyof T]: T[P]}

[P in keyof T]就是遍历 T 中的 key,T[P]就是当前的 key 的类型,其实[P in keyof T]: T[P]就是把 T 遍历了一遍,但是我们在属性前面加了个 readonly,这样我们调用这个泛型的时候,它就会把传入的类型的 key 遍历一遍,遍历的同时在前面加个 readonly,最终给我们返回一个新的类型。我们在调用的时候只需要这么用:

type Readonly = {  readonly [P in keyof T]: T[P]}type Person {    name: string    age: number}type ReadonlyPerson = Readonly

索引类型和映射类型除了能实现 Readonly,还能实现很多有意思的东西,我们平时在使用 TS 的时候,TS 已经内置了一些常用的辅助泛型,刚才的 Readonly 就是其一,另外还有很多,我从 TS 的类型定义文件里找了一些,这些泛型从简单到复杂的都有,但基本上都是用上面提到的两个类型实现的,下面我们一起来分析一下。

TS 常用的辅助泛型及其实现方式

首先来看第一个

/** * Make all properties in T optional */type Partial = {  [P in keyof T]?: T[P]}

相信这个泛型很多人都用过,就是把类型都变成可选的,和刚才的 Readonly 是类似的实现方式,只是这个是在后面加了个问号,这样一来属性就变成可选的了。
与之相对的还有一个 Required

/** * Make all properties in T required */type Required = {  [P in keyof T]-?: T[P]}

注意这个稍有点不同,它是-?,其实就是减去问号,这样就可以把问号去掉,从而变成必选的属性。再来看下一个。

/** * From T, pick a set of properties whose keys are in the union K */type Pickextends keyof T> = {  [P in K]: T[P]}

如果你理解了最开始的那个 pluck 函数,这个就很好理解了,我们传入 T 和 K,其中 K 是 T 的 keys 组成的联合类型,再看返回值[P in K]: T[P],就是把 K 遍历了一遍,同时赋值上原类型,那么综合来看 Pick 就是帮我们提取出某些类型的,比如通过Pick我们就可以得到{name: string},再来看下一个

/** * Exclude from T those types that are assignable to U */type Exclude = T extends U ? never : T

这个泛型传入一个 T 和 U,然后它判断了 T 是否属于 U,属于的话返回 never 否则返回原类型 T,注意 never 在最终的类型中是不会存在的,所以它可以帮助我们消除某些属性,其实这个 Exclude 就是消除了T extends U的类型,比如我们使用Exclude,最终会得到'a',与之相反的有:

/** * Extract from T those types that are assignable to U */type Extract = T extends U ? T : never

这个正好相反,是从 T 中取出 U 中拥有的类型。有了 Exclude,我们就可以和 Pick 结合来实现另外一个:

/** * construct a type with the properties of T except for those in type K. */type Omitextends keyof any> = Pick>

这个泛型是先使用了Exclude,去除了 keyof T 中的 K,然后又使用 Pick 取出了这些类型,这样我们就可以从 T 中去除 K 里面包含的 keys 了,达到和 Pick 相反的效果。我们再来看另一个稍微复杂一点的

type NonNullObject = Pick<  O,  {    [K in keyof O]: O[K] extends null | undefined ? never : K  }[keyof O]>

这个不是 TS 内置的类型,但也是一个很有用的类型,我们来一点一点分析。首先这个泛型使用了 Pick,我们知道 Pick 就是取出一些属性,我们先看传给 Pick 的第二个参数

{  [K in keyof O]: O[K] extends null | undefined ? never : K}[keyof O]

它遍历了 O 的 keys,然后进行了一个判断,如果是extends null | undefined则返回 never,否则返回 K,K 就是 O 中的 key 值,注意这里和之前的一些泛型有些不一样,之前的都是O[K],而这里的属性的值还是 K,最终我们得到的是类似K:K这样的东西,比如{name: string, age: null}这个,经过上面的转化会变成{name:'name', age:never},可能有些小伙伴还不清楚为什么要这样转换,我们接着往下分析,经过这个转换之后,又进行了一个操作[keyof O],对于 Person,keyof O 就是'name'|'age',那么这里就就是{name:'name', age:never}['name'|'age'],这样就很清晰了,其实就是一个取值操作,这样我们就可以得到'name'|never,还记得 never 的特性吗,它可以帮我们消除一些类型,那么最终的就是'name',这也是为什么我们写成类似 K:K 这样,就是要把 null|undefined 对应的 key 转换成 never,然后再通过 keyof 把他们全都取出来,别忘了最外面还有一个 Pick,这样我们就从原始类型中去除了 null|undefined。另外还有一个比较有用的是 ReturnType

/** * Obtain the return type of a function type */type ReturnType any> = T extends (  ...args: any) => infer R  ? R  : any

它可以帮我们取到函数返回值的类型,这个 ReturnType 接收的一个参数是函数,然后进行了一个判断T extends (...args: any) => infer R,就是判断是否是函数,这里有个东西是 infer,通过这个操作符我们可以获取 R 的引用,就是函数的返回值,最终再把 R 返回出去,就获得了函数 T 的返回值。其实除了我分析的这些泛型,TS 还内置了其他的很多泛型,比如还有获取函数的参数的,获取构造函数类型的,总的来说各种泛型基本上都可以用索引类型和映射类型实现。希望大家看过这篇文章后能多多使用这两种类型,在自己的项目里也能开发一些常用的辅助泛型,来提升工作效率。本文完~

typescript索引类型_TypeScript的索引类型与映射类型,以及常用工具泛型的实现相关推荐

  1. python映射类型包括哪三种_Python映射类型

    映射对象用于将哈希表值映射到任意对象.在python中,有一种称为字典的映射类型.这是可变的. 字典的键是任意的.作为值,我们可以使用不同种类的元素,例如列表,整数或任何其他可变类型的对象. 一些与字 ...

  2. ElasticSearch系列——倒排索引、删除映射类型、打分机制、配置文件、常见错误

    文章目录 1 倒排索引 2 删除映射类型 一 前言 二 什么是映射类型? 三 为什么要删除映射类型? 四 映射类型的替代方法 4.1 将映射类型分开存储在索引中 4.2 自定义类型字段回到顶部 五 没 ...

  3. Hibernate - Java 类型, Hibernate 映射类型及 SQL 类型之间的对应关系

    [1]常见的映射关系表 常见的映射对应关系如下表所示: Hibernate映射类型 Java类型 标准SQL类型 字节大小 integer/int java.lang.Integer/int INTE ...

  4. typescript索引类型_TypeScript类型声明书写详解

    本文总结一下TypeScript类型声明的书写,很多时候写TypeScript不是问题,写类型就特别纠结,我总结下,我在使用TypeScript中遇到的问题.如果你遇到类型声明不会写的时候,多看看lo ...

  5. typescript索引类型_typescript入门:高级类型

    学习typescript中 ,有一个小伙伴提出了一个问题 const a = { a:'1', b:'2', c:'3' } 复制代码 如何取到每个键上的值 ,组成一个联合类型 ? 即得到一个类型为 ...

  6. typeScript面试必备之-通识七:typeScript中的可索引接口(数组,对象)+类类型接口...

    可索引接口:数组.对象的约束 (不常用) ts定义数组的方式 var arr:number[]=[2342,235325]var arr1:Array<string>=['111','22 ...

  7. TypeScript基础入门 - 接口 - 可索引的类型

    转载地址 TypeScript基础入门 - 接口 - 可索引的类型 项目实践仓库 https://github.com/durban89/typescript_demo.git tag: 1.0.11 ...

  8. 元素隐式具有 “any” 类型,因为类型“Shared”没有索引签名问题解决思路

    最近在构建一个typescript项目时如下代码在项目框架里vscode报错元素隐式具有 "any" 类型,因为类型"Shared"没有索引签名;很有意思的是当 ...

  9. mysql单列索引和多列索引_mysql索引类型 normal, unique, full text

    问题1:mysql索引类型normal,unique,full text的区别是什么? normal:表示普通索引 unique:表示唯一的,不允许重复的索引,如果该字段信息保证不会重复例如身份证号用 ...

最新文章

  1. Oracle创建命名空间和新用户
  2. IDEA中安装MyBatis Log Plugin插件完整显示执行的mybatis的sql语句
  3. linux比较10个文件是否一致,Linux中文件管理--cmp--比较两个文件是否有差异
  4. 导入已有项目到svn
  5. java图片滚动特效_js图片各种滚动效果
  6. diy 单片机 自动浇花_造个自动浇花装置(完)
  7. python基础系列教程——python中的字符串和正则表达式全解
  8. Windows 底层驱动级 Anti-Rootkit 工具 ScDetective 源代码
  9. sbt命令行常用命令
  10. 微信计步器怎么不计步_微信运动不计步,如何解决
  11. 如何制作PE盘和系统启动盘
  12. springboot 解决 woff2、ttf 跨域无法解析问题
  13. CSS实现平行四边形
  14. 神州战神系列装系统过程,其他电脑大同小异
  15. Spring Security简单SSO
  16. linux怎么sftp传文件,Linux 如何使用SFTP命令传输文件
  17. MySQL数据库(基础)
  18. afterlogic 7.0.1 php,免费webmail 7.0.1(after logic 最后一个免费版本)
  19. Web前端开发技术课程大作业: 关于美食的HTML网页设计——HTML+CSS+JavaScript在线美食订餐网站html模板源码30个页面:
  20. rnnlm源码分析 二

热门文章

  1. ABAP webservice和log on对话框相关的实现细节调试
  2. DOCX file format element list
  3. another CRM inbound debug - 另一个CRM中间件的调试记录
  4. Context node attribute expand trouble shooting guide
  5. SAP Hybris Enterprise Commerce的一些有用链接 - 保证持续更新
  6. 为什么CRM Opportunity的删除会触发一个通向BW系统的RFC
  7. 100行代码让您学会JavaScript原生的Proxy设计模式
  8. java+mysql性能优化_Java培训实战教程之mysql优化
  9. m.2接口和nvme区别_透明款散热不好,那么ORICO 全铝NVMe固态硬盘盒了解一下?
  10. hmc如何进入aix系统_linux系统中如何进入退出vim编辑器,方法及区别