TypeScript 学习笔记 — 数组常见的类型转换操作记录(十四)
- 获取长度 length
type LengthOfTuple<T extends any[]> = T["length"];
type A = LengthOfTuple<["B", "F", "E"]>; // 3
type B = LengthOfTuple<[]>; // 0
- 取第一项 FirstItem
type FirstItem<T extends any[]> = T[0];
type A = FirstItem<[string, number, boolean]>; // string
type B = FirstItem<["B", "F", "E"]>; // 'B'
- 取最后一项 LastItem
type LastItem<T extends any[]> = T extends [...any, infer R] ? R : never;
type A = LastItem<[string, number, boolean]>; // boolean
type B = LastItem<["B", "F", "E"]>; // 'E'
type C = LastItem<[]>; // never
- 删除第一项 shift
type Shift<T extends any[]> = T extends [infer L, ...infer R] ? R : T;
type A = Shift<[1, 2, 3]>; // [2,3] . [never,2,3]
type B = Shift<[1]>; // []
type C = Shift<[]>; // []
- 向后追加 Push
type Push<T extends any[], K> = [...T, K];
type A = Push<[1, 2, 3], 4>; // [1,2,3,4]
type B = Push<[1], 2>; // [1, 2]
- 翻转 ReverseTuple
通过递归完成,先声明一个F默认结果集,每次递归时,在将L第一个值添加在F展开之前,递归结束返回F
type ReverseTuple<T extends any[], F extends any[] = []> = T extends [infer L, ...infer R] ? ReverseTuple<R, [L, ...F]> : F;
type A = ReverseTuple<[string, number, boolean]>; // [boolean, number, string]
type B = ReverseTuple<[1, 2, 3]>; // [3,2,1]
type C = ReverseTuple<[]>; // []
- 拍平 Flat
通过递归完成,一个一个判断是否是数组,是数组继续 Flat,反之返回
type Flat<T extends any[], F extends any[] = []> = T extends [infer L, ...infer R]
? [...(L extends any[] ? Flat<L> : [L]), ...Flat<R>] // 判断第一个是否是数组
: [];
type A = Flat<[1, 2, 3]>; // [1,2,3]
type B = Flat<[[[[[[[1]]]]]], [2, 3], [4, [5, [6]]]]>; // [1,2,3,4,5,6]
type C = Flat<[]>; // []
type D = Flat<[1]>; // [1]
- 复制 Repeat
在 ts 中操作与数量相关的,全部采用元素的长度来计算
C`表示需要的长度,`F`是最终结果,`F["length"]`,结果的长度,两者都是字面量类型,只有相等的时候才能 extends,满足条件返回 F,不满足接着 Repeat,再次递归时,需要将上次的`F`展开放进去,再放入原始数据`T
type Repeat<T, C, F extends any[] = []> = C extends F["length"] ? F : Repeat<T, C, [...F, T]>;
type A = Repeat<number, 3>; // [number, number, number]
type B = Repeat<string, 2>; // [string, string]
type C = Repeat<1, 1>; // [1]
type D = Repeat<0, 0>; // []
- 过滤 Filter
type Filter<T extends any[], I, F extends any[] = []> =
// 先取出第一项,判断是否满足,满足递归处理R
T extends [infer L, ...infer R]
? // 无论如何都要遍历剩余项, 所以在F这判断 是否要放到数组中,使用[]包裹是为了防止any分发返回联合类型
Filter<R, A, [L] extends [A] ? [...F, L] : F>
: F;
type A = Filter<[1, "BFE", 2, true, "dev"], number>; // [1, 2]
type B = Filter<[1, "BFE", 2, true, "dev"], string>; // ['BFE', 'dev']
type C = Filter<[1, "BFE", 2, any, "dev"], string>; // ["BFE", "dev"] | ["BFE", any, "dev"]
- 查找索引 FindIndex
实现思路:
- 找到两个值相等的一项,思考如何判断两个值是否相等?
- 最终要返回的是索引:内部构建一个数组,来记录当前遍历到了第几项
type errIsEqual<T, U, Success, Fail> = [T] extends [U] // 类型 + 结构
? [U] extends [T]
? Success
: Fail
: Fail;
type xx = errIsEqual<any, 1, true, false>; // 测试判等方法:返回了 true,但是 any != 1
// 因此由于any类型的特殊性, 需要判断两个类型是否相等。需要严格判断 两者的 类型 + 结构
type isEqual<T, U, Success, Fail> = [T] extends [U] // 类型 + 结构
? [U] extends [T]
? keyof T extends keyof U
? keyof U extends keyof T
? Success
: Fail
: Fail
: Fail
: Fail;
type yy = isEqual<any, 1, true, false>; // 测试判等方法:返回了 false
type FindIndex<T extends any[], I, F extends any[] = []> = T extends [infer L, ...infer R]
? isEqual<L, I, F["length"], FindIndex<R, I, [...F, never]>>
: never;
type a1 = [any, never, 1, "2", true];
type a2 = FindIndex<a1, 1>; // 2
type a3 = FindIndex<a1, 3>; // never
- 元组转枚举 TupleToEnum
- 默认情况下,枚举对象中的值就是元素中某个类型的字面量类型,
- 第二个参数为 true,枚举对象中值的类型就是元素类型中某个元素在元组中的 index 索引,也就是数字字面量类型(需要借助上一个案例中
type FindIndex)
type isEqual<T, U, Success, Fail> = [T] extends [U] // 类型 + 结构
? [U] extends [T]
? keyof T extends keyof U
? keyof U extends keyof T
? Success
: Fail
: Fail
: Fail
: Fail;
type FindIndex<T extends any[], I, F extends any[] = []> = T extends [infer L, ...infer R]
? isEqual<L, I, F["length"], FindIndex<R, I, [...F, never]>>
: never;
type TupleToEnum<T extends any[], I = false> = {
readonly [K in T[number]]: I extends true ? FindIndex<T, K> : K;
};
// 默认情况下
type a1 = TupleToEnum<["MacOS", "Windows", "Linux"]>;
// { readonly MacOS: "MacOS", readonly Windows: "Windows", readonly Linux: "Linux" }
// 第二个参数为true
type a2 = TupleToEnum<["MacOS", "Windows", "Linux"], true>;
// { readonly MacOS: 0, readonly Windows: 1, readonly Linux: 2 }
- 截取 slice
实现思路:
- 判断是否到达开始位置 S,到达就往结果集中添加,反之就舍弃
- 判断是否到达结束位置 E,没到就往结果集中添加,反之就返回
type Slice<
T extends any[],
S extends number, // 开始位置
E extends number = T["length"], // 结束位置
SA extends any[] = [], // 用于记录是否到达 S
EA extends any[] = [], // 用于记录是否到达 E
F extends any[] = [] // 结果集
> = T extends [infer L, ...infer R]
? SA["length"] extends S // 判断是否满足 开始index,满足放入集合F中
? EA["length"] extends E // 判断是否满足 结束index,表示截取完成
? [...F, L] // 都满足,直接结束,返回结果集 + L
: Slice<R, S, E, SA, [...EA, null], [...F, L]> // SA 匹配到索引就不在累积添加null
: Slice<R, S, E, [...SA, null], [...EA, null], F> // 添加null 是为了占位,撑开数组长度
: F;
type A1 = Slice<[any, never, 1, "2", true, boolean], 0, 2>; // [any,never,1] ---0到2
type A2 = Slice<[any, never, 1, "2", true, boolean], 1, 3>; // [never,1,'2'] ---1到3
type A3 = Slice<[any, never, 1, "2", true, boolean], 1, 2>; // [never,1] ---1到2
type A4 = Slice<[any, never, 1, "2", true, boolean], 2>; // [1,'2',true,boolean] ---2到结束
type A5 = Slice<[any], 2>; // [] ---2到结束
type A6 = Slice<[], 0>; // []
- 删除指定长度,并追加新的元素 splice
实现思路:
- 判断是否到达开始位置 S,到达就往结果集中添加,反之就舍弃并且 SA++
- 判断是否到达需要删除的个数,到达就拼接删除前的结果集 F + 需要添加的集合 I + 删除后剩下的集合,反之 DC++
type Splice<
T extends any[],
S extends number, // 开始index
C extends number, // 需要删除的个数
I extends any[] = [], // 需要插入的元素集合
SA extends any[] = [], // 用于记录是否到达 S
DC extends any[] = [], // 用于记录 已经删除的个数
F extends any[] = [] // 结果集
> = T extends [infer L, ...infer R]
? SA["length"] extends S // 判断是否满足 开始index,满足放入集合F中
? DC["length"] extends C // 判断是否满足 要删除的长度
? [...F, ...I, ...T] // 都满足,返回F删除前的结果集 + 需要添加的集合 + 删除后剩下的集合
: Splice<R, S, C, I, SA, [...DC, null], F> // 不满足删除的长度。DC++
: Splice<R, S, C, I, [...SA, null], DC, [...F, L]> // 不满足开始index。SA++
: F;
type A1 = Splice<[string, number, boolean, null, undefined, never], 0, 2>; // 从0开始删2个 [boolean,null,undefined,never]
type A2 = Splice<[string, number, boolean, null, undefined, never], 1, 3>; // 从1开始删3个 [string,undefined,never]
type A3 = Splice<[string, number, boolean, null, undefined, never], 1, 2, [1, 2, 3]>; // 从1开始删2个,添加另外3个 [string,1,2,3,null,undefined,never]
小总结
在 ts 中跟数量有关,全部会采用 length
凡是涉及遍历,全部采用递归[infer L,...infer R]来处理
['a','b','c'][number] 可以取到对应索引的 key 值
判断两个类型是否相等,需要考虑 any 类型,因此需要使用严格判等,类型 + 结构
TypeScript 学习笔记 — 数组常见的类型转换操作记录(十四)的更多相关文章
- Typescript 学习笔记六:接口
中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...
- Typescript 学习笔记二:数据类型
中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...
- Typescript 学习笔记七:泛型
中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...
- Typescript 学习笔记五:类
中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...
- Typescript 学习笔记四:回忆ES5 中的类
中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...
- Typescript 学习笔记三:函数
中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...
- Typescript 学习笔记一:介绍、安装、编译
前言 整理了一下 Typescript 的学习笔记,方便后期遗忘某个知识点的时候,快速回忆. 为了避免凌乱,用 gitbook 结合 marketdown 整理的. github地址是:ts-gitb ...
- TypeScript学习笔记(八):1.5版本之后的模块和命名空间
我之前有写过TS1.5版本之前的“模块”的笔记:TypeScript学习笔记(七):模块 但是TS这里的模块和在ECMAScript 2015里的模块(即JS原生支持了模块的概念)概率出现了混淆,所以 ...
- golang学习笔记16 beego orm 数据库操作
golang学习笔记16 beego orm 数据库操作 beego ORM 是一个强大的 Go 语言 ORM 框架.她的灵感主要来自 Django ORM 和 SQLAlchemy. 目前该框架仍处 ...
- Dart学习笔记-运算符-条件表达式-类型转换
Dart学习笔记-运算符-条件表达式-类型转换 一.运算符 1.算术运算符 + (加)- (减)* (乘)/ (除)~/ (取整) %(取余) 2.关系运算符 == (等等) != (不等) > ...
随机推荐
- 翻下旧资料,发现96年考过foxbase二级
翻下旧资料,找到 96年通过二级考试的证书,那时考的是Foxbase,一路走来,从最早用netware+dos无盘站+foxbase做订单系统,库存管理系统,再到使用记事本码asp网站,PB+orac ...
- 功能测试--Fiddler
Fiddler(更推荐Charles,很好用) 1.fiddler是什么?------客户端的所有请求都要先经过fiddler,然后转发到服务器:反之,服务器的所有响应,也会先经过fiddler,然后 ...
- JavaScript 包装类
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- How to Change Reset Retrieve the WebLogic Server Administrator Password on WLS 10.3.6 or earlier
To change the Administrator password on WLS 10.3.6 or earlier, perform the following steps depending ...
- 简单的python格网算法算数据密集度demo
# 格网算法计算数据集区域数据密集度 import time import random import numpy as np import pandas as pd # 模拟数据集 def crea ...
- MySql 入门——日期计算
MySQL自带的日期函数TIMESTAMPDIFF计算两个日期相差的秒数.分钟数.小时数.天数.周数.季度数.月数.年数,当前日期增加或者减少一天.一周等等 SELECT TIMESTAMPDIFF( ...
- 2020.11.24 typeScript命名空间
命名空间:定义了标识符的可见范围,一个标识符可以在多个命名空间中定义,它在不同命名空间的含义是互不相干的.在一个新的命名空间可以定义任何新的标识符,它不会与已有的任何标识符发生冲突. 使用: 这个时候 ...
- MFS分布式存储特性及组件说明
1.MFS MooseFS是一个具有冗余容错功能的分布式网络文件系统,它将数据分别存放在多个物理服务器或单独磁盘或分区上,确保一份数据有多个备份副本,然而对于访问MFS的客户端或者用户来说,整个分布式 ...
- 安卓自动化查看包名和activity名的方法
1,cmd下,adb shell "dumpsys window | grep mCurrentFocus" 2,CMD中输入adb logcat ActivityManager: ...
- 有时候用uniapp写项目时发现,Map组件在安卓真机可以缩放和移动,但是在ios真机就不行
如果你的地图组件是放到popup组件里,是用弹框打开的,如何ios端不能缩放, 那你一定要看下这个弹框的层级是否比地图层级要高 z-index. 如果高于地图层级,那地图肯定是不能移动和缩放的