Generic范型

  • 前言
  • 1. Hello World之范型
  • 2. 范型的类型变量
  • 3. 范型类型/范型接口
  • 4. 范型类
  • 5. 范型约束
  • 6. 在泛型约束中使用类型参数
  • 7. 在泛型中使用类类型

前言

TS用了一段时间了,我目前对它的印象就是:加了类型的JS。那么我认为比较重要的、一定要好好学的一个特性就是范型,它能够让类型声明更加强大和通用,学好范型能够让TS编译器做更好的类型检查,提升我们写代码的效率和质量。官网文档相应内容点这里,(随缘翻译,不会一字一句翻,按自己理解来)。

软件工程中一个重要的部分就是构造通用的组件,它们有定义完备和一致的API,并且有很强的可复用性。具备兼容性和可扩展性的组件是构建大型软件系统中不可或缺的部分,能为系统提供灵活性。

在C#和Java当中,构造通用组件的主要工具就是范型,范型就是能够在一类特定规则的类型(而非某个具体的类型)上工作良好,使得用户可以使用自己的特定类型去使用通用组件。

1. Hello World之范型

什么是范型?

我们先从一个identify函数开始,这个函数简单地返回它接受到的东西,像一个echo命令:

function identity(arg: number): number {return arg;
}

上述代码表明identify函数接收一个number类型的参数,并且返回一个number类型的结果;

我们也可以使用any类型:

function identity(arg: any): any {return arg;
}

使用any就是一种范型,any做类型声明使得这个函数可以接受任何类型的参数,并且可能返回任意类型的结果。比如说传入一个number类型,identify也可能返回任意类型,比如string之类的。

然而这不是我们想要的,我们失去了对函数返回值的掌控。需要一种方式来指明函数入参和出参的关系;下面可以使用一个类型变量,一种工作在类型而不是具体值上的特殊变量:

function identity<Type>(arg: Type): Type {return arg;
}

上面代码中我们添加了一个类型变量Type,它指明了函数入参和返回值是同一种类型,我们还可以在函数体中使用这个类型。这就是范型,它工作在一系列类型上。和之前直接使用any来表示函数能接收任何类型不一样。
比如对于第一个函数,我们只需要传入Type = number就行了,注意类型变量的值要包裹在一对尖括号中<>

let output = identity<number>(3);

这时我们就能确保output也是一个number类型的值了。这是范型的第一种调用方式;第二种直接让编译器做类型推断,这也是比较常用的使用方式(对于项目中比较复杂的业务类型,建议都进行显式声明):

let output = identity(3);

因为函数返回值output和入参3类型一样,所以即使不传递范型,我们也知道output的类型是number。

2. 范型的类型变量

对于上面identity这样的泛型函数来说,编译器会强制我们在函数体中将这些参数视为任何类型,这个意思是对于identity来说,如果我们想使用具体类型的一些api,比如string类型的.length,那么编译器会报错:

function loggingIdentity<Type>(arg: Type): Type {console.log(arg.length);  // ts-error: Property 'length' does not exist on type 'Type'.return arg;
}

这是因为我们没有对范型做任何约束,这样的Type本身就可以是任何类型,如果用户传入number,而number没有.length属性,那么就会触发错误,因此TS提示我们这段代码有风险,这也是TS为啥好用的原因,减少我们出bug的机率。

我们可以把入参和返回值的类型都改成任何类型的数组Type[],这下再使用.length就没问题了。

function loggingIdentity<Type>(arg: Type[]): Type[] {console.log(arg.length);return arg;
}// orfunction loggingIdentity<Type>(arg: Array<Type>): Array<Type> {console.log(arg.length); // Array has a .length, so no more errorreturn arg;
}

基本认识有了,下面开始构造我们自己的范型。

3. 范型类型/范型接口

本节我们探索函数类型本身,以及如何构造范型类型,或者说范型接口。

范型函数的类型和无范型的函数差不多,和函数的声明很相似,先列出其入参和出参的类型,入参类型 => 出参类型

function identity<Type>(arg: Type): Type {return arg;
}let myIdentity: <Type>(arg: Type) => Type = identity;

在类型声明中,范型的名称可以任意取,只要声明中保持一致就行:

function identity<Type>(arg: Type): Type {return arg;
}let myIdentity: <Input>(arg: Input) => Input = identity;

也可以和对象一样包裹在一对花括号中来写范型,**{ 入参类型 : 出参类型 } **:

function identity<Type>(arg: Type): Type {return arg;
}let myIdentity: { <Type>(arg: Type): Type } = identity;

我们可以把类型声明提取出来

interface GenericIdentityFn {<Type>(arg: Type): Type;
}function identity<Type>(arg: Type): Type {return arg;
}let myIdentity: GenericIdentityFn = identity;

接着把范型定义提取到整个接口最外面,使其对内部所有类型可见,GenericIdentityFn就是一个范型类型了,例如下面我们将其变量声明为number,那么所有用到Type的地方就都确定下来了:

interface GenericIdentityFn<Type> {(arg: Type): Type;
}function identity<Type>(arg: Type): Type {return arg;
}let myIdentity: GenericIdentityFn<number> = identity;

注意现在情况有点不一样了,不同于上一节描述了一个范型函数,现在是一个使用部分泛型类型的非泛型函数签名。在使用GenericIdentityFn时,需要指定其具体的类型参数(比如这里的number),从而把具体的类型传递到下游函数中去。

理解清楚什么时候将类型参数直接放在调用签名上,何时将它放在类型本身上,有助于描述类型的哪些方面是泛型的。(<>加在哪,范型就在哪)

4. 范型类

除了范型接口外,我们还可以定义范型类。和接口类型,类的范型也是用一对尖括号<>包起来,放在类名后。

class GenericNumber<NumType> {zeroValue: NumType;add: (x: NumType, y: NumType) => NumType;
}let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function (x, y) {return x + y;
};

同样的,在类名后声明的类型变量,整个类内部都可以使用这种类型。另外,一个类有静态属性和实例属性,范型只能在实例属性中使用(原型链上的静态属性属于这个类的上游了,范型,也就是类型变量,只能往下游传递)。

5. 范型约束

还记得第2节的例子吗,当时我们在函数中使用了.length属性,ts提示报错了,因为我们对函数声明的范型没有加任何约束,当传递不含长度属性的类型时会导致错误。

function loggingIdentity<Type>(arg: Type): Type {console.log(arg.length);  // ts-error: Property 'length' does not exist on type 'Type'.return arg;
}

那么能否给范型增加约束,使传递的类型必须满足拥有.length这个属性呢?我们首先声明一个接口表明范型必须满足的约束,然后在书写范型的时候用extends关键词给范型增加约束:

interface Lengthwise {length: number;
}function loggingIdentity<Type extends Lengthwise>(arg: Type): Type {console.log(arg.length); // Now we know it has a .length property, so no more errorreturn arg;
}

此时因为传递进来的类型变量Type必须继承自Lengthwise(也就是拥有length属性),这是函数中就可以直接使用该属性了。
同样因为我们增加对范型的约束条件,这个函数能够接受的类型就不再是任何类型了,而是Array, string等满足Lengthwise的类型才行。

另外,我们声明了函数参数也是继承自Lengthwise的,那么给函数传值的时候也必须传Lengthwise声明的必传参:

loggingIdentity({ length: 10, value: 3 });

6. 在泛型约束中使用类型参数

7. 在泛型中使用类类型

Ts官方文档翻译-Generic范型相关推荐

  1. Generic Data Access Objects -范型DAO类设计模式

    Generic Data Access Objects 普通数据访问对象,这个是Hibernate官方网站上面的一个DAO类的设计模式,基于JDK5.0范型支持,文章地址如下: http://www. ...

  2. 范型 DAO范型的应用

    当你偶然路过这里时,我假定你已经很明白 java 中范型和 DAO 模式了.当然,我也会顺便唠叨几句范型和 DAO 模式,只是它们不会这篇随笔的重点.我早先在 DW 上看到一篇蛮不错的文章 不要重复 ...

  3. Jess 7.2p2——Java平台规则引擎官方文档翻译1

    Jess 7.2p2--Java平台规则引擎官方文档翻译1 本博客转载自本体小本营. 1.入门 1.1.需求 在使用Jess前请先确认JVM是否正确安装并能正常工作. 使用JessDE集成开发环境,你 ...

  4. 官方文档翻译《The Libra Blockchain》之执行交易(二)

    译自:官方文档翻译<The Libra Blockchain>之执行交易,第三小节.本作品采用知识共享署名-非商业性使用-相同方式共享 3.0 中国大陆许可协议进行许可. 执行交易 执行一 ...

  5. 官方文档翻译《The Libra Blockchain》之执行交易(一)

    译自:官方文档翻译<The Libra Blockchain>之执行交易,第三小节.本作品采用知识共享署名-非商业性使用-相同方式共享 3.0 中国大陆许可协议进行许可. 执行交易 Lib ...

  6. Ionic Framework 5官方文档翻译

    Ionic Framework 5官方文档翻译 入门 Ionic Framework 5特点 Ionic Framework是什么? 核心概念 构建你的第一个应用 先进的Web应用 浏览器支持 版本控 ...

  7. About HTTP Live Streaming官方文档翻译 [iOS]

    新博客地址:About HTTP Live Streaming官方文档翻译 [iOS] 版权声明:本文为博主原创翻译,如需转载请注明出处. 苹果源文档地址 - 点击这里 About HTTP Live ...

  8. Java中的generics范型

    简介generics范型 Generics 范型 构造范型 范型的优点 Wildcards 使用通配符 Generics 范型 范型意味着参数化类型.所操作的数据类型都被指定为一个参数 范型可以为接口 ...

  9. 【iOS官方文档翻译】iOS蓝牙的基本概念

    之前写了[iOS官方文档翻译]iOS的蓝牙连接.数据接收及发送一文,介绍了怎样进行蓝牙通讯,但是很多基本概念没有进行解释,看起来可能有点吃力,所以现在再翻译一篇苹果对官方蓝牙4.0一些基本概念介绍的文 ...

最新文章

  1. ViewPager单页显示3个item
  2. 《深入理解计算机系统》第十章——系统级I/0
  3. 【Codeforces】913C Party Lemonade (贪...)。
  4. 注意链栈next指针的指向,与队列不同:
  5. 【网络安全】如何使用keimpx检测网络环境中的有效凭证
  6. hash和history两种模式的区别+SpringBoot的特定配置
  7. 关于bootstrap的treeview不显示多选(复选框)的问题,以及联动选择的问题,外加多选后取值...
  8. 普罗米修斯java_springboot集成普罗米修斯(Prometheus)的方法
  9. 【jvm】jvisualvm 离线下载安装插件
  10. 【MySQL】MySQL 执行 PROCEDURE ANALYSE 报错 ERROR 1064 (42000)
  11. 如何在本机使用正式版的SAP Business One的Common库的Lisence服务
  12. Java基础--序列化和反序列化
  13. Ubuntu 12.10方便操作套件
  14. 临时切换淘宝源下载包
  15. 关于用C#调用C++的dll中的函数,获取字符串返回值的一些细节
  16. cad导出pdf_MxCAD云图DWG转PDF
  17. 惠普2t服务器硬盘,HP DL388 Gen8系列服务器硬盘超过2T的分区方法
  18. 能链发布数字藏品为用户定制专属数字礼物
  19. 搭建网站服务器必须开443端口,HTTPS端口必须一定要443吗?
  20. android imageview stretchblt,想知道Bmp图像的缩小放大用Tcanvas.StretchDraw还是用StretchBlt函数?(50分)...

热门文章

  1. halo个人博客搭建
  2. 微博粉丝精灵_微博、抖音、豆瓣等被点名通报!
  3. 微星B450迫击炮+AMD R5 3600 装机
  4. 关于目标与执行力的思考
  5. Django框架的基本应用
  6. i39 1.6 [TNT] 特别版 Mac iPhone 手机铃声制作软件
  7. mysql 原子自增_mysql自增锁_33
  8. Cena、Lemon自动AC机
  9. 2014年8月英语总结
  10. Kong(二)通过案例快速了解使用