ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
&emsp;&emsp;泛型是程序设计语言中的一种风格或范式,相当于类型模板,允许在声明类、接口或函数等成员时忽略类型,而在未来使用时再指定类型,其主要目的是为它们提供有意义的约束,提升代码的可重用性。 ## 一、泛型参数 当一个函数需要能处理多种类型的参数和返回值,并且还得约束它们之间的关系(例如类型要相同)时,就可以采用泛型的语法,如下所示。 ~~~ function send<T>(data: T): T { return data; } ~~~ &emsp;&emsp;函数名称后面跟了,其中把T称为泛型参数或泛型变量,表示某种数据类型。注意,T只是个占位符,可以命名的更含语义,例如TKey、TValue等。在使用时,既可以指定类型,也可以利用类型推论自动确定类型,如下所示。 ~~~ send<number>(10); //指定类型 send(10);   //类型推论 ~~~ &emsp;&emsp;当需要处理T类型的数组时,可以像下面这么写。 ~~~ function send<T>(data: T[]): T[] { return data; } send<number>([1, 2, 3]); ~~~ &emsp;&emsp;当指定一个泛型函数的类型时,需要包含泛型参数,如下所示,其中泛型参数和函数参数的名称都可与定义时的不同。 ~~~ let func: <U>(data: U) => U = send; ~~~ &emsp;&emsp;泛型参数还支持传递多个,只需在声明时增加类型占位符即可。在下面的示例中,将T和U合并成了一个元组类型,还有许多其它用法,将在后面讲解。 ~~~ function send<T, U>(data: [T, U]): [T, U] { return data; } send<number, string>([1, "a"]); ~~~ ## 二、泛型接口 &emsp;&emsp;在接口中,可利用泛型来约束函数的结构,如下所示,接口中声明的调用签名包含泛型参数。 ~~~ interface Func { <T>(str: T): T; } function send<T>(str: T): T { return str; } let fn: Func = send; ~~~ &emsp;&emsp;泛型参数还可以作为接口的一个参数存在,即把用尖括号包裹的泛型参数移到接口名称之后,如下所示。 ~~~ interface Func<T> { (str: T): T; } function send<T>(str: T): T { return str; } let fn: Func<string> = send; ~~~ &emsp;&emsp;当把Func接口作为类型使用时,需要向其传入一个类型,例如上面赋值语句中的string。 ## 三、泛型类 &emsp;&emsp;泛型类与泛型接口类似,也是在名称后添加泛型参数,如下所示,其中send属性中的“=>”符号不表示箭头函数,而是用来定义方法的返回值类型。 ~~~ class Person<T> { name: T; send: (data: T) => T; } ~~~ &emsp;&emsp;在实例化泛型类时,需要为其指定一种类型,如下所示。 ~~~ let person = new Person<string>(); person.send = function(data) { return data; } ~~~ &emsp;&emsp;注意,类的静态部分不能使用泛型参数。 ## 四、泛型约束 &emsp;&emsp;在使用泛型时,由于事先不清楚参数的数据类型,因此不能随意调用它的属性或方法,甚至无法对其使用运算符。在下面的示例中,访问了data的length属性,但由于编译器无法确定它的类型,因此就会报错。 ~~~ function send<T>(data: T): T { console.log(data.length); return data; } ~~~ &emsp;&emsp;TypeScript允许为泛型参数添加约束条件,从而就能调用相应的属性或方法了,如下所示,通过extends关键字约束T必须是string的子类型。 ~~~ function send<T extends string>(data: T): T { console.log(data.length); return data; } ~~~ &emsp;&emsp;在添加了这个约束之后,send()函数就无法接收数字类型的参数了,如下所示。 ~~~ send("10"); //正确 send(10); //错误 ~~~ **1)创建类的实例** &emsp;&emsp;在使用泛型创建类的工厂函数时,需要声明T类型拥有构造函数,如下所示。 ~~~ class Programmer { } function create<T>(ctor: {new(): T}): T { return new ctor(); } create(Programmer); ~~~ &emsp;&emsp;用“{new(): T}”替代原先的类型占位符,表示可以被new运算符实例化,并且得到的是T类型,另一种相同作用的写法如下所示。 ~~~ function create<T>(ctor: new()=>T): T { return new ctor(); } ~~~ **2)多个泛型参数** &emsp;&emsp;在TypeScript中,多个泛型参数之间也可以相互约束,如下所示,创建了基类Person和派生类Programmer,并将create()函数中的T约束为U的子类型。 ~~~ class Person { } class Programmer extends Person { } function create<T extends U, U>(target: T, source: U): T { return target; } ~~~ &emsp;&emsp;当传递给create()函数的参数不符合约束条件时,就会在编译阶段报错,如下所示。 ~~~ create(Programmer, Person); //正确 create(Programmer, 10); //错误 ~~~ ***** > 原文出处: [博客园-TypeScript躬行记](https://www.cnblogs.com/strick/category/1561745.html) [知乎专栏-TypeScript躬行记](https://zhuanlan.zhihu.com/pwts2019) 已建立一个微信前端交流群,如要进群,请先加微信号freedom20180706或扫描下面的二维码,请求中需注明“看云加群”,在通过请求后就会把你拉进来。还搜集整理了一套[面试资料](https://github.com/pwstrick/daily),欢迎浏览。 ![](https://box.kancloud.cn/2e1f8ecf9512ecdd2fcaae8250e7d48a_430x430.jpg =200x200) 推荐一款前端监控脚本:[shin-monitor](https://github.com/pwstrick/shin-monitor),不仅能监控前端的错误、通信、打印等行为,还能计算各类性能参数,包括 FMP、LCP、FP 等。