前端深入理解Typescript泛型概念


Posted in Javascript onMarch 09, 2020

首先介绍一下泛性的概念

泛型程序设计(generic programming)是程序设计语言的一种风格或范式。泛型允许程序员在强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型。

泛型是指在定义函数,接口或者类的时候,不预先定义好具体的类型,而在使用的时候在指定类型的一种特性。

先举一个简单的例子

假设我们定义一个函数,它可以接收一个number类型做为参数,并且返回一个number类型。

function genericDemo(data: number): number {
  return data;
}

按照以上的写法是没有问题的,但是如果我们要接受一个string并返回一个string呢?如果逻辑一样还要在写一遍吗?就像下面这样。

function genericDemo(data: string): string {  
  return data;
}

这显然代码是很冗余的,我们还有不使用any的写法吗?答案是显然易见的,可以使用范型的写法,就像下面这样。

function genericDemo<T>(data: T):T {
  return data;
}

我们在函数名称genericDemo后面声明了范型变量<T>,他用于捕获调用该函数时传入的参数类型(例如:number),之后我们就可以使用这个类型。 之后我们再次使用了T当做返回值类型。现在我们可以知道参数类型与返回值类型是相同的了。这允许我们跟踪函数里使用的类型的信息。

多个类型参数

我们在定义范型的时候,也可以一次定义多个类型参数,像下面这样。

function swap<T, U>(tuple: [T, U]):[U, T] {
  return [tuple[1], tuple[0]];
}

泛型接口

我们先定义一个范型接口Identities,然后定义一个函数identities()来使用这个范型接口

interface Identities<T, U> {
  id1: T;
  id2: U;
}

我在这里使用T和U作为我们的类型变量来演示任何字母(或有效的字母数字名称的组合)都是有效的类型—除了常规用途之外,您对它们的调用没有任何意义。

我们现在可以将这个接口应用为identity()的返回类型,修改我们的返回类型以符合它。我们还可以console.log这些参数和它们的类型,以便进一步说明:

function identities<T, U> (arg1: T, arg2: U): Identities<T, U> {
  console.log(arg1 + ": " + typeof (arg1));
  console.log(arg2 + ": " + typeof (arg2));
  let identities: Identities<T, U> = {
  id1: arg1,
  id2: arg2
 };
 return identities;
}

我们现在对identity()所做的是将类型T和U传递到函数和identity接口中,从而允许我们定义与参数类型相关的返回类型。

范型变量

使用泛型创建像identity这样的泛型函数时,编译器要求你在函数体必须正确的使用这个通用的类型。 换句话说,你必须把这些参数当做是任意或所有类型。

我们先看下之前例子

function genericDemo<T>(data: T):T {
  return data;
}

如果我们想同时打印出data的长度。 我们很可能会这样做

function genericDemo<T>(data: T):T {
  console.log(data.length); // Error: T doesn't have .length
  return data;
}

如果这么做,编译器会报错说我们使用了data的.length属性,但是没有地方指明data具有这个属性。 记住,这些类型变量代表的是任意类型,所以使用这个函数的人可能传入的是个数字,而数字是没有.length属性的。

现在假设我们想操作T类型的数组而不直接是T。由于我们操作的是数组,所以.length属性是应该存在的。 我们可以像创建其它数组一样创建这个数组:

function genericDemo<T>(data: Array<T>):Array<T> {
  console.log(data.length);
  return data;
}

范型类

我们还可以在类属性和方法的意义上使类泛型。泛型类确保在整个类中一致地使用指定的数据类型。例如下面这种在React Typescript项目中的写法。

interface Props {
  className?: string;
  ...
}

interface State {
  submitted?: bool;
  ...
}

class MyComponent extends React.Component<Props, State> {
  ...
}

我们在这里使用与React组件一起使用的泛型,以确保组件的props和state是类型安全的。

泛型约束

我们先看一个常见的需求,我们要设计一个函数,这个函数接受两个参数,一个参数为对象,另一个参数为对象上的属性,我们通过这两个参数返回这个属性的值,比如:

function getValue(obj: object, key: string){
  return obj[key] // error
}

我们会得到一段报错,这是新手 TypeScript 开发者常常犯的错误,编译器告诉我们,参数 obj 实际上是 {},因此后面的 key 是无法在上面取到任何值的。

因为我们给参数 obj 定义的类型就是 object,在默认情况下它只能是 {},但是我们接受的对象是各种各样的,我们需要一个泛型来表示传入的对象类型,比如T extends object:

function getValue<T extends object>(obj: T, key: string) {
 return obj[key] // error
}

这依然解决不了问题,因为我们第二个参数 key 是不是存在于 obj 上是无法确定的,因此我们需要对这个 key 也进行约束,我们把它约束为只存在于 obj 属性的类型,这个时候需要借助到后面我们会进行学习的索引类型进行实现 <U extends keyof T>,我们用索引类型 keyof T 把传入的对象的属性类型取出生成一个联合类型,这里的泛型 U 被约束在这个联合类型中,这样一来函数就被完整定义了:

function getValue<T extends object, U extends keyof T>(obj: T, key: U) {
 return obj[key] // ok
}

另外提一个多重泛型约束的写法,可以当作拓展:

interface firstInterface {
  first(): number
}

interface secondInterface {
  second(): string
}

class Demo<T extends firstInterface & secondInterface >{
  ...
}

在泛型里使用类类型

在TypeScript使用泛型创建工厂函数时,需要引用构造函数的类类型。比如:

function create<T>(type: {new(): T; }): T {
  return new type();
}

参数type的类型{new(): T}就表示此泛型T是可被构造的,在被实例化后的类型是泛型 T。

总结

到此这篇关于前端深入理解Typescript泛型概念的文章就介绍到这了,更多相关Typescript 泛型内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
javascript 极速 隐藏/显示万行表格列只需 60毫秒
Mar 28 Javascript
Javascript Select操作大集合
May 26 Javascript
用最通俗易懂的代码帮助新手理解javascript闭包 推荐
Mar 01 Javascript
jquery 实现二级/三级/多级联动菜单的思路及代码
Apr 08 Javascript
js生成动态表格并为每个单元格添加单击事件的方法
Apr 14 Javascript
详解AngularJS过滤器的使用
Mar 11 Javascript
jQuery实现弹窗居中效果类似alert()
Feb 27 Javascript
在 Vue 项目中引入 tinymce 富文本编辑器的完整代码
May 04 Javascript
利用vue-i18n实现多语言切换效果的方法
Jun 19 Javascript
JavaScript函数重载操作实例浅析
May 02 Javascript
vue 自定指令生成uuid滚动监听达到tab表格吸顶效果的代码
Sep 16 Javascript
微信小程序实现底部弹出框
Nov 18 Javascript
js实现无缝轮播图效果
Mar 09 #Javascript
js实现无缝轮播图
Mar 09 #Javascript
基于vue+echarts 数据可视化大屏展示的方法示例
Mar 09 #Javascript
原生js实现瀑布流效果
Mar 09 #Javascript
原生JS实现贪吃蛇小游戏
Mar 09 #Javascript
微信小程序 wx.getUserInfo引导用户授权问题实例分析
Mar 09 #Javascript
在Vue中实现随hash改变响应菜单高亮
Mar 09 #Javascript
You might like
php_pdo 预处理语句详解
2016/11/21 PHP
php生成图片缩略图功能示例
2017/02/22 PHP
Laravel构建即时应用的一种实现方法详解
2017/08/31 PHP
基于jquery的动态创建表格的插件
2011/04/05 Javascript
jquery 设置元素相对于另一个元素的top值(实例代码)
2013/11/06 Javascript
3种Jquery限制文本框只能输入数字字母的方法
2014/12/03 Javascript
深入分析JSON编码格式提交表单数据
2015/06/25 Javascript
纯css实现窗户玻璃雨滴逼真效果
2015/08/23 Javascript
javascript实现简单的全选和反选功能
2016/01/05 Javascript
Javascript删除指定元素节点的方法
2016/06/21 Javascript
jq.ajax+php+mysql实现关键字模糊查询(示例讲解)
2018/01/02 Javascript
js简单粗暴的发布订阅示例代码
2021/01/23 Javascript
[01:25:09]2014 DOTA2国际邀请赛中国区预选赛 5 23 CIS VS DT第二场
2014/05/24 DOTA
python中异常报错处理方法汇总
2016/11/20 Python
Python3中使用PyMongo的方法详解
2017/07/28 Python
Python实现批量执行同目录下的py文件方法
2019/01/11 Python
numpy下的flatten()函数用法详解
2019/05/27 Python
Python 虚拟空间的使用代码详解
2019/06/10 Python
python pickle存储、读取大数据量列表、字典数据的方法
2019/07/07 Python
django框架用户权限中的session缓存到redis中的方法
2019/08/06 Python
win10子系统python开发环境准备及kenlm和nltk的使用教程
2019/10/14 Python
在Django中实现添加user到group并查看
2019/11/18 Python
JAVA及PYTHON质数计算代码对比解析
2020/06/10 Python
Python列表的深复制和浅复制示例详解
2021/02/12 Python
欧迪办公美国官网:Office Depot
2016/08/22 全球购物
医院后勤自我鉴定
2013/10/13 职场文书
大学四年职业生涯规划书范文
2014/01/02 职场文书
职业道德模范事迹材料
2014/08/24 职场文书
2014年幼儿园安全工作总结
2014/11/10 职场文书
师德师风事迹材料
2014/12/20 职场文书
英语导游词
2015/02/13 职场文书
2015年幼儿园个人工作总结
2015/04/25 职场文书
css 中多种边框的实现小窍门
2021/04/07 HTML / CSS
jdbc使用PreparedStatement批量插入数据的方法
2021/04/27 MySQL
python中requests库+xpath+lxml简单使用
2021/04/29 Python
Python中使用tkFileDialog实现文件选择、保存和路径选择
2022/05/20 Python