泛型
软件工程中,我们不仅要创建一致的定义良好的API,同时也要考虑可重用性。 组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。
在像C#和Java这样的语言中,可以使用泛型
来创建可重用的组件,一个组件可以支持多种类型的数据。 这样用户就可以以自己的数据类型来使用组件。
泛型的特点
- 支持多种类型的数据(不特定的数据类型)。
- 传入的类型与返回的类型应该是相同的。
- 使用类型变量且指标是类型,不是值。
- 泛型的类型取决于调用的时候传入的参数类型。
- 对不特定类型数据的校验。
不用泛型的话,这个函数可能是下面这样:
function identity(arg: number): number {
return arg;
}
或者,我们使用any
类型来定义函数:
function identity(arg: any): any {
return arg;
}
这里存在一个问题,就是如果返回值是any会导致这个函数可以接收任何类型的arg
参数,即any放弃了类型检查,这样就丢失了一些信息:传入的类型与返回的类型应该是相同的。如果我们传入一个数字,我们只知道任何类型的值都有可能被返回。
因此,我们需要一种方法使返回值的类型与传入参数的类型是相同的。 这里,我们使用了 类型变量,它是一种特殊的变量,只用于表示类型而不是值。
function identity<T>(arg: T): T {
return arg;
}
泛型的两种使用
定义了泛型函数后,可以用两种方法使用:
- 传入所有的参数,包含类型参数:
let output = identity<string>("myString"); // type of output will be 'string'
这里我们明确的指定了T
是string
类型,并做为一个参数传给函数,使用了<>
括起来而不是()
。
2.利用了类型推论 -- 即编译器会根据传入的参数自动地帮助我们确定T的类型
let output = identity("myString"); // type of output will be 'string'
泛型类
class MinClass<T>{
public list:T[] = [];
add(value:T):void{
list.push(value);
}
min():T{
let minValue = list[0];
for(var i = 0;i<this.list.length;i;i++){
if(minNum>this.list[i]){
minNum = this.list[i];
}
}
}
}
var m1 = new MinClass<number>(); //实例化类,并且规定类的T(泛型)代表number
m1.add(12);
m1.add(3);
m1.add(9);
alert(m1.min());//3
var m2 = new MinClass<string>(); //实例化类,并且规定类的T(泛型)代表string
m2.add(c);
m2.add(a);
m2.add(b);
alert(m2.min());//a
泛型接口
首先定义一个函数类型接口
//定义一个非泛型的函数类型接口
interface ConfigFn{
(value1:string,value2:string):string;
}
var setData:Config = function(value1:string,value2:string):string{
return value1+value2;
}
setData('name','zsf');
很明显这种函数类型借口函数的传入参数只能是string
所以为了解决以上问题,引入泛型接口:
第一种定义泛型接口的方法:
//定义一个泛型函数类型的接口
interface ConfigFn{
<T>(value:T):T;
}
var getData:ConfigFn = function(value:T):T{
return value;
}
getData<string>('zsf');
getData<number>(123);
第二种定义泛型接口的方法:
//定义一个泛型函数类型的接口
interface ConfigFn<T>{
(value:T):T;
}
var getData:ConfigFn<T> = function(value:T):T{
return value;
}
var mygetData1:ConfigFn<string>=getData;
mygetData1:ConfigFn<string>(‘20’);
var mygetData2:ConfigFn<number>=getData;
mygetData2:ConfigFn<string>(20);
把类作为参数类型的泛型类
- 定义一个类
- 把类作为参数来约束传入的类型
/*
定义一个User的类,这个类的作用就是映射数据库字段
让后定义一个mysqlDb的类,这个类用于操作数据库
然后把User类作为参数传到MysqlDb中
*/
class User{
username:string | undefined;
password:string | undefined;
}
class MysqlDb{
//用User类来检验传入的对象的合法性
add(user:User):boolean{
console.log(user);
return true;
}
}
var u = new User();
u.username = 'wj';
u.password = '我最帅';
var Db = new MysqlDb();
Db.add(u);
此时User 对象能够顺利存入,但是如果 要传个其他类型的对象,比如说要传一个文章类型的对象,那MysqlDb 就要重新定义数据类型,很显然,这样做不满足泛型,所以要重新定义Mysql类。
class MysqlDb<T>{
//用User类来检验传入的对象的合法性
add(info:T):boolean{
console.log(info);
return true;
}
}
class User{
username:string | undefined;
password:string | undefined;
}
var u = new User();
u.username = 'wj';
u.password = '我最帅';
var Db = new MysqlDb<User>();
Db.add(u);
class ArticleCate{
title:string | undefined;
desc:string | undefined;
status:number | undefined;
constructor(params:{
title:string | undefined,
desc:string | undefined,
status?:number | undefined
}){
this.title = title;
this.desc = desc;
this.status = status;
}
}
var at = new ArticleCate({
title:'武侠',
desc:'张三丰成神之路'
});
var aDb = new MysqlDb<ArticleCate>();
aDb.add(at);