前端深入理解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 相关文章推荐
json的前台操作和后台操作实现代码
Jan 20 Javascript
解决jquery的datepicker的本地化以及Today问题
May 23 Javascript
利用jQuery的deferred对象实现异步按顺序加载JS文件
Mar 17 Javascript
JS防止用户多次提交的简单代码
Aug 01 Javascript
javascript移出节点removeChild()使用介绍
Apr 03 Javascript
JavaScript 弹出子窗体并返回结果到父窗体的实现代码
May 28 Javascript
JS+HTML5实现的前端购物车功能插件实例【附demo源码下载】
Oct 17 Javascript
jQuery 点击获取验证码按钮及倒计时功能
Sep 20 jQuery
Vue使用Canvas绘制图片、矩形、线条、文字,下载图片
Apr 26 Javascript
刷新页面后让控制台的js代码继续执行
Sep 20 Javascript
javascript实现摄像头拍照预览
Sep 30 Javascript
vue实现简单全选和反选功能
Sep 15 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循环创建目录示例分享(php创建多级目录)
2014/03/04 PHP
ThinkPHP中where()使用方法详解
2016/04/19 PHP
PHP5.6新增加的可变函数参数用法分析
2017/08/25 PHP
PHP内部实现打乱字符串顺序函数str_shuffle的方法
2019/02/14 PHP
php常用日期时间函数实例小结
2019/07/04 PHP
javascript数组组合成字符串的脚本
2021/01/06 Javascript
基于jquery的无刷新分页技术
2011/06/11 Javascript
JQuery each打印JS对象的方法
2013/11/13 Javascript
javascript数组随机排序实例分析
2015/07/22 Javascript
使用RequireJS库加载JavaScript模块的实例教程
2016/06/06 Javascript
JavaScript中的toString()和toLocaleString()方法的区别
2017/02/15 Javascript
Angular 4 依赖注入学习教程之FactoryProvider的使用(四)
2017/06/04 Javascript
node.js操作MongoDB的实例详解
2017/10/11 Javascript
Vue 项目代理设置的优化
2018/04/17 Javascript
详解基于vue的服务端渲染框架NUXT
2018/06/20 Javascript
在vue中使用防抖和节流,防止重复点击或重复上拉加载实例
2019/11/13 Javascript
小程序跳转到的H5页面再跳转回跳小程序的方法
2020/03/06 Javascript
uniapp实现可以左右滑动导航栏
2020/10/21 Javascript
[48:00]完美世界DOTA2联赛循环赛 Forest vs Inki BO2第二场 11.04
2020/11/04 DOTA
在DigitalOcean的服务器上部署flaskblog应用
2015/12/19 Python
Python绘制正余弦函数图像的方法
2018/08/28 Python
Python进阶之自定义对象实现切片功能
2019/01/07 Python
手写一个python迭代器过程详解
2019/08/27 Python
python3 配置logging日志类的操作
2020/04/08 Python
python+adb+monkey实现Rom稳定性测试详解
2020/04/23 Python
澳大利亚顶级美发和美容贸易超市:glamaCo
2020/01/19 全球购物
医院办公室主任职责
2013/12/29 职场文书
总经理秘书岗位职责
2014/03/17 职场文书
文科毕业生自荐书范文
2014/04/17 职场文书
大学活动总结范文
2014/04/29 职场文书
2014年最新领导班子整改方案
2014/09/27 职场文书
2015年社区工会工作总结
2015/05/26 职场文书
建国大业电影观后感
2015/06/01 职场文书
个人收入证明格式
2015/06/24 职场文书
CSS使用伪类控制边框长度的方法
2022/01/18 HTML / CSS
联想win10摄像头打不开怎么办?win10笔记本摄像头打不开解决办法
2022/04/08 数码科技