TypeScript 类型编程之索引类型递归去掉可选修饰

目录
  • 前言
  • 总结

前言

这两天东东遇到一个 TS 的问题,跑来问我。

问题是这样的:

这样一个 interface,想取出 userInfo 的类型来:

interface Result{
    data?: {
        userInfo?: {
            name: string;
        }
    }
}

他是这样取的:

type userInfo = Result['data']['userInfo'];

但是会报错:

说是 userInfo 不在这个联合类型上。

这很正常,因为可选索引的含义就是值和 undefined 的联合类型 value | undefined。

于是他问我应该怎么取?

我和他说这个问题有两种不同复杂度的解决方案,有简单的有复杂的,问他想听哪个。

他说想听简单的,于是我告诉他这样写:

type userInfo = Required<Required<Result>['data']>['userInfo']

Required 是 ts 内置的高级类型,是把索引类型的所有可选修饰去掉的。

所以每一层用 Required 处理一下再取索引的值就可以了。

但是这样虽然简单,当取的层数多了要写很多次 Required,也挺麻烦的。

然后东东又问我如果是复杂的那个,要怎么写?

我和他说复杂的那个写起来麻烦一些,但好处是用起来简单,不管多少层都只需要处理一次:

首先要知道 Required 是怎么实现的:

他这里用到了映射类型的语法,作用是对索引类型做一些修改,生成新的索引类型。

P in keyof T 就是遍历索引类型 T 中的所有索引 P,用来构造新的索引类型,值保持不变,也就是 T[P]。

构造的过程中可以加上可选的修饰、也可以去掉可选的修饰,还可以对值和索引做一些修改。

所以和 Required 相对的 Partial 就是这样实现的:

我们想一次处理完所有层级,都把可选的修饰给去掉,那就要递归处理,也就是这样:

type DeepRequired<Obj> = {
    [Key in keyof Obj]-?: IsOptional<Key, Obj> extends never? Obj[Key]: DeepRequired<Obj[Key]>
}

遍历索引类型 Obj 中的所有索引 Key,通过 -? 去掉可选,然后对值要做一下判断,如果还是可选索引,那就递归处理。

那怎么实现这个 IsOptional 的判断索引是否是可选的高级类型呢?

判断某个类型要根据他的性质来,可选的性质就是 value | undefined,也就是说可能是空。

可以这样来实现可选的判断:

type IsOptional<Key extends keyof Obj, Obj> =
    {} extends Pick<Obj, Key> ? Key : never;

Obj 是索引类型,Key 是他的某个索引,因为可选索引的性质是可能为空,所以 {} 就可能是索引类型的子类型。

这里的 Pick 也是内置的高级类型,作用是取出一部分索引构造新的索引类型:

同样是通过映射类型的语法实现的:

这里 a 可能是没有的,那当没有的时候不就是 {} 么? 所以可以用 {} extends Pick<Obj, Key> 来判断是不是可选索引。

综上,递归去掉索引类型的可选修饰就是这样实现的:

type IsOptional<Key extends keyof Obj, Obj> =
    {} extends Pick<Obj, Key> ? Key : never;

type DeepRequired<Obj> = {
    [Key in keyof Obj]-?: IsOptional<Key, Obj> extends never? Obj[Key]: DeepRequired<Obj[Key]>
}

我们测试一下:

现在只要处理一次,就可以取任意层级的索引值了,方便了很多。

其实写成这样就可以用了,但是有时候你会遇到这样的问题:

TS 没有把最终的结果计算出来。

这个是 TS 的机制,默认是懒计算的,用不到的不会计算。

那怎么让他计算出最终结果呢?

加上一段逻辑触发计算就可以了,比如 xxx extends any 这种肯定会成立的条件判断:

再测试一下你就会发现 TS 计算出了最终的结果:

总结

想取一个可选索引的值,需要先用 Required 把索引类型去掉可选然后再取。但是当层数多了的话,这样一层层处理挺麻烦的,可以用类型编程递归处理下。

用映射类型的语法去掉索引类型的可选修饰,判断值的类型,如果还是可选的索引,那就继续递归的处理。

判断可选索引是通过可选的性质来的,可选索引的值是 value | undefined, 所以 {} extends Pick<Obj, Key> 成立的话就代表这个 Key 是可选的。

可能会遇到类型没有全部计算的问题,这是 TS 的机制,默认是懒计算的,可以加上 xx extends any 这种不影响结果的条件类型来触发计算。

层层用 Required 处理在层数少的情况下比较简单,但层数多了的时候还是递归处理更方便一些,而且这样的高级类型是可以复用的,可以用在别的地方,这也是类型编程的好处。

到此这篇关于TypeScript 类型编程之索引类型递归去掉可选修饰的文章就介绍到这了,更多相关TypeScript 索引类型递归内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • TypeScript对于Duck类型和模块命名空间应用

    目录 一.TypeScript 鸭子类型 二.TypeScript 命名空间 三.TypeScript 模块 四.类型脚本声明文件 一.TypeScript 鸭子类型 Duck类型是一种动态类型和多态形式.在这种风格中,对象的有效语义不是通过从特定类继承或实现特定接口来确定的,而是通过“当前方法和属性的集合”来确定的. var object_name = { key1: "value1", // 标量 key2: "value", key3: function()

  • 使用typescript类型实现ThreeSum

    目录 前言 思路整理 实现TwoSum 实现减法 元祖中是否包含差值 递归元组 测试 实现ThreeSum 实现排序 实现ThreeSum 测试 前言 本文执行环境typescript,版本4.7.4 不使用typescript的计算能力,通过类型来实现ThreeSum 思路整理 实现ThreeSum之前我们先降低下难度,实现TwoSum,因为TwoSum可以作为ThreeSum的基础泛型 TwoSum需要准备什么呢? 递归元组,模拟for循环 减法,递归过程中求出差值 对每一项差值判断是否存在

  • 使用typescript推导已有变量的盲盒类型详情

    目录 迁移盲盒 类型推导 基础类型的推导 对象的推导 数组的推导 函数的推导 完善推导 测试 迁移盲盒 当我们从JavaScript一键转换Typescript的时候,any便是最省事的做法,对于维护并不友好(虽然能跑就行),同时每个变量对于我们来说都是盲盒,它到底是什么类型? 类型推导 基础类型的推导 基础数据类型的类型推导还是挺简单的 let a = 1; type A = typeof a; // number; let b = '2' type B = typeof b; // stri

  • 使用typescript类型来实现快排详情

    目录 前言 元组快排 实现逻辑 实现数字的大小比较 实现 A 是否 小于或等于 B 实现 A 是否 大于或等于 B 实现Filter 优化Filter 重构数字的大小值比较 重构Filter 实现快排 测试快排 优化:负数 负数的判断 字符串转数字 获取负数的值 完善获取绝对值 重构数字的大小比较 重构快排 测试快排V2 前言 本文执行环境typescript,版本4.7.4 不使用typescript的计算能力,通过类型来实现快排 元组快排 能否将元组 [3, 1, 2, 4] 通过泛型转换成

  • 你需要知道的TypeScript高级类型总结

    目录 1. 字面量类型 (1)字符串字面量类型 (2)数字字面量类型 (3)布尔字面量类型 (4)模板字面量类型 2. 联合类型 (1)基本使用 (2)限制 (3)可辨识联合类型 3. 交叉类型 (1)基本实用 (2)使用场景 4. 索引类型 (1)索引类型查询操作符 (2)索引访问操作符 (3)应用 5. 条件类型 (1)基本概念 (2)创建自定义条件类型 (3)条件类型的类型推断 6. 类型推断 (1)基础类型 (2)多类型联合 (3)上下文类型 7. 类型保护 (1)instanceof

  • TypeScript中命名空间与模块化详情

    目录 一.模块 二.命名空间 三.区别 一.模块 TypeScript 与ECMAScript 2015 一样,任何包含顶级 import 或者 export 的文件都被当成一个模块 相反地,如果一个文件不带有顶级的import或者export声明,那么它的内容被视为全局可见的 例如我们在在一个 TypeScript 工程下建立一个文件 1.ts,声明一个变量a,如下: const a = 1 然后在另一个文件同样声明一个变量a,这时候会出现错误信息 提示重复声明a变量,但是所处的空间是全局的

  • TypeScript 类型编程之索引类型递归去掉可选修饰

    目录 前言 总结 前言 这两天东东遇到一个 TS 的问题,跑来问我. 问题是这样的: 这样一个 interface,想取出 userInfo 的类型来: interface Result{ data?: { userInfo?: { name: string; } } } 他是这样取的: type userInfo = Result['data']['userInfo']; 但是会报错: 说是 userInfo 不在这个联合类型上. 这很正常,因为可选索引的含义就是值和 undefined 的联

  • TypeScript 内置高级类型编程示例

    目录 TypeScript 类型编程 TypeScript 内置高级类型 Pick<Type, Keys> Exclude<UnionType, ExcludedMembers> ReturnType<Type> 更多类型体操学习 TypeScript 类型编程 TypeScript 的类型系统,最基本的是简单对应 JavaScript 的 基本类型,比如 string.number.boolean 等,然后是新增的 tuple.enum.复合类型.交叉类型.索引类型等

  • 利用TypeScript从字符串字面量类型提取参数类型

    目录 正文 挑战 需要掌握的内容 字符串字面量类型 模板字面量类型和字符串字面量类型 条件类型 函数重载和通用函数 着手解决问题 分割字符串字面量类型 参数语法部分的过滤 在对象类型里做一个映射 正文 挑战 我们先来做一个ts的挑战. 你知道如何为下面的app.get方法定义TypeScript类型吗? req.params是从传入的第一个参数字符串中提取出来的. 当你想对一个类似路由的函数定义一个类型时,这显得很有用,你可以传入一个带路径模式的路由,你可以使用自定义语法格式去定义动态参数片段(

  • 详解mysql索引总结----mysql索引类型以及创建

    关于MySQL索引的好处,如果正确合理设计并且使用索引的MySQL是一辆兰博基尼的话,那么没有设计和使用索引的MySQL就是一个人力三轮车.对于没有索引的表,单表查询可能几十万数据就是瓶颈,而通常大型网站单日就可能会产生几十万甚至几百万的数据,没有索引查询会变的非常缓慢.还是以WordPress来说,其多个数据表都会对经常被查询的字段添加索引,比如wp_comments表中针对5个字段设计了BTREE索引. 一个简单的对比测试 以我去年测试的数据作为一个简单示例,20多条数据源随机生成200万条

  • MySQL索引类型总结和使用技巧以及注意事项

    在数据库表中,对字段建立索引可以大大提高查询速度.假如我们创建了一个 mytable表: 复制代码 代码如下: CREATE TABLE mytable(   ID INT NOT NULL,    username VARCHAR(16) NOT NULL  ); 我们随机向里面插入了10000条记录,其中有一条:5555, admin. 在查找username="admin"的记录 SELECT * FROM mytable WHERE username='admin';时,如果在

  • 简单介绍下MYSQL的索引类型

    一.介绍一下索引的类型 Mysql常见索引有:主键索引.唯一索引.普通索引.全文索引.组合索引 PRIMARY KEY(主键索引) ALTER TABLE `table_name` ADD PRIMARY KEY ( `column` ) UNIQUE(唯一索引) ALTER TABLE `table_name` ADD UNIQUE (`column`) INDEX(普通索引)      ALTER TABLE `table_name` ADD INDEX index_name ( `colu

  • MySQL索引类型Normal、Unique和Full Text的讲解

    MySQL的索引类型有普通索引(normal),唯一索引(unique)和全文索引(full text),合理使用索引可大大提升数据库的查询效率,下面是三种类型的索引的介绍 normal:这是最基本的索引,它没有任何限制,MyIASM中默认的BTREE类型的索引,是我们大多数情况下用到的索引. unique:表示唯一的,不允许重复的索引,如果该字段信息保证不会重复.例如身份证号用作索引时,可设置为unique. full text : 表示全文搜索的索引,仅可用于 MyISAM 表. FULLT

  • Mysql索引类型与基本用法实例分析

    本文实例讲述了Mysql索引类型与基本用法.分享给大家供大家参考,具体如下: 索引 MySQL目前主要有以下几种索引类型: 普通索引 唯一索引 主键索引 组合索引 全文索引 - 普通索引 是最基本的索引,它没有任何限制. CREATE INDEX IndexName ON `TableName`(`字段名`(length)) - 唯一索引 与前面的普通索引类似,不同的就是:索引列的值必须唯一,但允许有空值.如果是组合索引,则列值的组合必须唯一. CREATE UNIQUE INDEX index

  • MongoDB索引类型汇总分享

    目录 单字段索引 在单个字段上创建升序索引 在嵌入式字段上创建索引 在内嵌文档上创建索引 复合索引 多键索引 文本索引 2dsphere索引 创建测试数据 添加2dsphere索引 利用2dsphere索引查询多边形里的点 利用2dsphere索引查询球体上定义的圆内的点 2d索引 哈希索引 MongoDB 4.2官方支持索引类型如下: 单字段索引 复合索引 多键索引 文本索引 2dsphere索引 2d索引 geoHaystack索引 哈希索引 单字段索引 在单个字段上创建升序索引 hando

  • Mysql 数据库结构及索引类型

    目录 前言 数据库索引的数据结构 聚集索引 辅助索引 索引管理 联合索引 覆盖索引 总结 前言 数据库索引是mysql数据库中重要的组成部分,是数据库查询数据速度提升的关键,本文将介绍数据库索引的一些内容. 数据库索引的数据结构 在数据库中的索引方法中,有TREE和HASH两种方法,HASH是经常使用的,本文中主要介绍TREE的数据结构.B+Tree 的高度一般是2-4层,也就是说查找一条数据记录,最多使用 2-4 次 IO,当前一般的机械磁盘每秒至少可以做 100 次 IO, 2-4 词的 I

随机推荐