TypeScript 内置高级类型编程示例


Posted in Javascript onSeptember 23, 2022

TypeScript 类型编程

TypeScript 的类型系统,最基本的是简单对应 JavaScript 的 基本类型,比如 string、number、boolean 等,然后是新增的 tuple、enum、复合类型、交叉类型、索引类型等 增强类型

这里会有一个问题,就是函数声明支持不同类型的重复编写问题,比如我的一个函数要接收一个数组,然后从中取中一个元素。

一旦我们传入的数组类型不同,都要写多一个 type 别名,未免太繁琐。

type getStrItem = (items: string[]) => string;
type getNumItem = (items: number[]) => number;
// ... 每增加一种类型都要写多了一个 type 别名
const getStrFirst: getStrItem = (a) => {
    return a[0];
}

为解决这个问题,TypeScript 引入了 泛型,让类型也能成为参数了。

type getItem<T> = (items: T[]) => T
const getStrFirst: getItem<string> = (a) => {
    return a[0];
}

上面的 T 就是一个类型参数,当我们通过 类型别名<具体类型> 形式(上面代码对应 getItem<string>),我们就能得到一个具体的类型了。

鉴于 JavaScript 太灵活,TypeScript 实现的是结构类型系统,我们又觉得泛型的简单推到 T 的粒度还是不够细,我们希望能够获取 T 内部的结构。

于是,TypeScript 在泛型的基础上,又提供了 类型编程,通过一些语法,我们可以拿到 T 下更细粒度的类型,或通过判断拿到其他类型。

这个也被大家戏称为 类型体操。可能是因为实现起来花里胡哨像是在参加体操大赛的原因。

总结一下,从类型能力上的增强的过程来说,就是:

基本类型 -> 泛型 -> 类型编程(类型体操)

TypeScript 内置高级类型

TS 代码版本为 4.8.2

下面我们来看一下 TypeScript 内置的几个高级类型,它们用了类型编程。

Pick<Type, Keys>

Pick 的作用是,从 T 类型(对象类型)中,提取出 K(联合类型)圈定的 key,返回一个新的对象类型。

TypeScript 内置高级类型编程示例

这里我们通过 Pick 提取了需要的 pos 和 radius 物理信息属性。

看看 Pick 的实现:

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

首先我们看等号左侧的 <T, K extends keyof T>,类型参数有两个,T 和 K。

先说类型参数命名

类型变量命名和写 JS 变量一样,随意起名。但建议首字母大写,以防止和一些关键字混淆(比如 extends, as, infer),这些关键词都是小写的。

T 通常代表一个要被分析的类型(Type),K 通常代表对象属性名(Key)。就像数学中函数的 x 和 y 一样,想不到好的命名就用这俩。

keyof 是类型运算符,用于提取对象的属性(key),然后拼装成联合类型。

TypeScript 内置高级类型编程示例

extends 用于限制类型参数的范围。比如 <T extends string> 表示 T 类型必须是 string 的子类,像字面量的 "a" 或 string 都是 string 的子类。如果不是 string 子类,编译无法通过。

还有一种是 extends ? : 的类似 JS 中三元运算符的语法,它在等号的右侧,用于实现条件判断。它和前面提到的 extends 不是同一样东西,后面我会说到。

Ok,我们整体看看 <T, K extends keyof T> 代表什么意思。它表示传入 T 和 K 两个类型参数,然后 K 必须是 T 的属性组成的联合类型中的一部分。

我们再看看等号右边 { [P in K]: T[P]; };,它是对类型进行 重映射

in 用于对联合类型进行遍历。也就是遍历我们需要用到的 key,作为索引 P,然后它的值还是用对应的 T[P]。

Exclude<UnionType, ExcludedMembers>

Exclude 的作用是,从联合类型中剔除掉一些类型。

实现如下:

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

这里涉及到一个经常用到的 条件语法extends ? :,你可以把它类比为 JS 中的三元表达式(即 condition ? a : b)。

为了更好的讲解,我们实现一个类型 IsNumber,判断一个类型是否为数值类型。

type IsNumber<T> = T extends number ? true : false;
// 使用
type A = IsNumber<1> // true
type B = IsNumber<"str"> // false

T extends number 判断 T 是否为 number 的子类,如果是的话,返回 true,否则返回 false。

需注意和前面的类型参数上 extends 是完全不同的东西。

回到我们的 Exclude,逻辑就很清楚了,就是判断 T 是否为 U 的子类,如果是的话,返回 never(效果是被丢弃);否则返回 T。

TypeScript 内置高级类型编程示例

你是不是有点奇怪结果,逻辑看起来不应该是 "a" | "b" | "c" 不是 "b" 的子类,返回 "a" | "b" | "c" 吗?怎么编程了 "a" | "c"?

其实这是联合类型的特殊逻辑,如果联合类型使用了 extends,它就会被打散,变成多个独立的类型进行判断,最后再组合起来

所以真正逻辑是, "a" | "b" | "c" 被打散,变成依次判断 "a" 、"b"、"c" 是否为 "b" 的子类,分别得到  "a" 、never、"c",然后联合起来,就变成了  "a" | "c"。

ReturnType<Type>

获取函数类型的返回值类型。

TypeScript 内置高级类型编程示例

实现为:

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

等号左侧的 (...args: any) => any 代表一个任意函数类型,用于限制传入参数的类型。

然后我们看到了一个新的关键词 infer,代表引用的意思,用于类型推导。

extends 和 infer 搭配,可以实现 模式匹配,如果 extends 匹配成功,infer 就能推导获得对应的类型。

如果你了解  JS 的正则表达式,你会发现它们很像,infer 好比是捕获组。

'ABC'.replace(/A(.)C/, '$1') 
// 'B'。提取了模式上匹配的一个字符串

T extends (...args: any) => infer R ? R : any; 中,我们给返回值部分设置了 infer,并提供了一个局部变量 R。

如果 extends 条件判断是继承关系,那么变量 R 就会被赋值函数的返回值。

后面的判断为真的分支(? 后面的表达式)就能拿到这个 R。判断为假的分支就无法拿到,因为匹配失败了。

这个 extends + infer 其实就是类型体操的精髓,可以在传入类型 T 继续拆分,拿到更细粒度的类型。

更多类型体操学习

还有更多的类型编程的技巧因为篇幅原因就不说了,比如还有:

  • as 运算符可以做类型索引的重映射;
  • 通过数组的 "length" 可以实现数字运算;
  • 通过递归实现循环逻辑;
  • 一些特殊的类型(比如 never)的处理等。

TypeScript 的类型是图灵完备的,可以实现各种判断、循环、加减的逻辑。当然某些逻辑实现起来很繁琐就是了。

它的语法也是与众不同:它做了 “压缩”。一个类型的编程只是一个表达式,需要用 extend ? : 的方式不停嵌套实现逻辑。TS 类型体操学起来,某种意义上确实有点像学一门新的语言,而且有那么一点古怪。

以上就是TypeScript 内置高级类型编程示例的详细内容,更多关于TypeScript 内置类型的资料请关注三水点靠木其它相关文章!

Javascript 相关文章推荐
抽出www.templatemonster.com的鼠标悬停加载大图模板的代码
Jul 11 Javascript
在一个js文件里远程调用jquery.js会在ie8下的一个奇怪问题
Nov 28 Javascript
js 通过cookie实现刷新不变化树形菜单
Oct 30 Javascript
jQuery 1.9.1源码分析系列(十)事件系统之绑定事件
Nov 19 Javascript
AngularJS路由切换实现方法分析
Mar 17 Javascript
javascript深拷贝的原理与实现方法分析
Apr 10 Javascript
关于react中组件通信的几种方式详解
Dec 10 Javascript
vue.js中实现登录控制的方法示例
Apr 23 Javascript
vue .js绑定checkbox并获取、改变选中状态的实例
Aug 24 Javascript
详解webpack编译速度提升之DllPlugin
Feb 05 Javascript
Vue自定义指令写法与个人理解
Feb 09 Javascript
JS实现查找数组中对象的属性值是否存在示例
May 24 Javascript
详解Anyscript开发指南绕过typescript类型检查
Sep 23 #Javascript
js基于div丝滑实现贝塞尔曲线
Sep 23 #Javascript
TS 类型兼容教程示例详解
Sep 23 #Javascript
TS 类型收窄教程示例详解
Sep 23 #Javascript
JavaScript实现简单的音乐播放器
Aug 14 #Javascript
vue实现简易音乐播放器
Aug 14 #Vue.js
Vue3实现简易音乐播放器组件
Aug 14 #Vue.js
You might like
php使用curl存储cookie的示例
2014/03/31 PHP
PHP实现获取某个月份周次信息的方法
2015/08/11 PHP
PHP Yaf框架的简单安装使用教程(推荐)
2016/06/08 PHP
ThinkPHP删除栏目(实现批量删除栏目)
2017/06/21 PHP
jQuery 选择表格(table)里的行和列及改变简单样式
2012/12/15 Javascript
详细介绍8款超实用JavaScript框架
2013/10/25 Javascript
js 日期比较相关天数代码
2014/04/02 Javascript
js锁屏解屏通过对$.ajax进行封装实现
2014/07/31 Javascript
JavaScript和HTML DOM的区别与联系及Javascript和DOM的关系
2015/11/15 Javascript
AngularJs bootstrap搭载前台框架——准备工作
2016/09/01 Javascript
jQuery实现的超链接提示效果示例【附demo源码下载】
2016/09/09 Javascript
使用JavaScript实现链表的数据结构的代码
2017/08/02 Javascript
js实现网页的两个input标签内的数值加减(示例代码)
2017/08/15 Javascript
vue中路由验证和相应拦截的使用详解
2017/12/13 Javascript
TypeScript中的方法重载详解
2019/04/12 Javascript
如何用webpack4.0撸单页/多页脚手架 (jquery, react, vue, typescript)
2019/06/18 jQuery
详解小程序如何改变onLoad的执行时机
2019/11/01 Javascript
js实现滑动滑块验证登录
2020/07/24 Javascript
微信小程序入门之指南针
2020/10/22 Javascript
Python中的面向对象编程详解(上)
2015/04/13 Python
Python+树莓派+YOLO打造一款人工智能照相机
2018/01/02 Python
将Django项目部署到CentOs服务器中
2018/10/18 Python
Windows系统下PhantomJS的安装和基本用法
2018/10/21 Python
kafka-python批量发送数据的实例
2018/12/27 Python
解决pycharm下os.system执行命令返回有中文乱码的问题
2019/07/07 Python
python模拟键盘输入 切换键盘布局过程解析
2019/08/15 Python
python FTP批量下载/删除/上传实例
2019/12/22 Python
关于jupyter打开之后不能直接跳转到浏览器的解决方式
2020/04/13 Python
浅谈TensorFlow中读取图像数据的三种方式
2020/06/30 Python
如何在mac版pycharm选择python版本
2020/07/21 Python
浅谈css3中的前缀
2016/07/20 HTML / CSS
canvas里面如何基于随机点绘制一个多边形的方法
2018/06/13 HTML / CSS
管理学专业个人求职信范文
2013/12/13 职场文书
2015教师年度思想工作总结
2015/04/30 职场文书
公司业务员管理制度
2015/08/05 职场文书
vue组件冲突之引用另一个组件出现组件不显示的问题
2022/04/13 Vue.js