泛型的定义
泛型, 软件开发过程中,我们不仅要创建一致的定义良好的api, 同时也要考虑可重用性。 组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供十分灵活的功能。 在像C#和java这样的语言中,可以使用泛型来创建可重用的组件, 一个组件可以支持多种数据类型的数据。这样用户就可以以自己的数据类型来使用组件。 通俗理解,泛型就是解决 类 接口 方法的复用性、以及对不特定数据类型的支持泛型函数
有一个需求: 传入指定的参数类型 返回这个指定的参数类型 比如 要求参数类型是 string 返回 string 要求参数类型是 number返回 number
// 只能返回string 的函数 function getData(val:string):string{ return val + "返回字符串"; } // 只能返回number 的函数 function getData1(val:number):number{ return val; // 返回数字 }
这里用了两个函数去实习功能,如果还需要返回 bloolean 类型是不是还需要再添加多一个函数去实现, 这样实现的话会出现了代码的冗余
有人就会想到用any类型去实现功能,
function getData2(val:any):any{ return 123; // 返回number } getData2("string");
用any可以实现功能, 但是会出现这样的错误, 我传入string类型, 返回了number ,不符合要求, 并且使用any类型就是抛弃typeScript的初衷了,放弃了类型检查
使用泛型解决问题:泛型可以支持不特定类型, 实现要求传入类型和返回的类型一致
// T 表示泛型, 具体什么类型是调用的时候决定的 function getData<T>(val:T):T{ return val; } // 调用: // number类型, 返回number 类型 getData<number>(100); // string 类型 getData<string>("张三"); getData<boolean>(123); // 错误写法 定义Boolean类型,不能传入number类型的参数
如果要求函数返回必须为string类型的 泛型函数写法:
// 泛型函数, 指定返回string类型 function getData<T>(val:T):string{ return val + "val"; } getData<number>(234); getData<string>("张三");
通过这两个例子可以看出 泛型 T 代表的是一种数据类型, 具体是什么数据类型,就是要调用该方法的时候传入指定的类型来确定的 。
getData<T> getData<string> 该函数里面的所有T 都是泛型
泛型类
需求:比如有个最小的堆算法, 需要同时支持返回数字和字符串两种类型, 通过类的泛型来实现// 不适用泛型类的写法 class MinClass{ public list:number[] = []; add(num:number):void{ this.list.push(num); } min():number { var minNum = this.list[0]; for(var i = 0; i < this.list.length; i++) { if(minNum > this.list[i]) { minNum = this.list[i]; } } return minNum; } } var m = new MinClass(); m.add(2); m.add(50); m.add(1); console.log(m.min()); // 输出: 1
这个列子可以实现的是number类型的最小堆算法,对于类型校验,不能使用string的类型
看看 泛型类的实现
// 泛型类 class MinClass<T>{ public list:T[] = []; add(num:T):void{ this.list.push(num); } min():T { var minNum = this.list[0]; for(var i = 0; i < this.list.length; i++) { if(minNum > this.list[i]) { minNum = this.list[i]; } } return minNum; } } var m = new MinClass<number>(); // 实例化 类, 并且指定了泛型 的类型 为number m.add(2); m.add(50); m.add(1); console.log(m.min()); // 输出: 1 var s = new MinClass<string>(); s.add("n"); s.add("a"); s.add("z"); console.log(s.min()); // 输出: a 码值计算
使用泛型了, 可以写入number类型和string类型
--- 泛型类--- 进阶
要求:定义一个类,把类当作参数,来约束传入的数据类型
实现:定义一个user的类,这个类的作用是映射数据库字段, 然后定义个mysqlDb的类,用于操作数据库,然后把User类当作参数传入到mysqlDb执行
// 映射数据库字段的实体类 class User{ public username:string | undefined; public pasword:string | undefined; constructor() { } } // 数据库操作类型 class MysqlDb{ // 添加数据 add(user:User):boolean{ return true; } // 删除数据 // 修改数据 // ---- } var u = new User(); u.username = "张三"; u.pasword = "123"; var Db = new MysqlDb(); Db.add(u); // 把这个user添加到数据库中
这个 MysqlDb 可以执行 user类 的添加等操作, 同样的问题:这个MysqlDb 只能对user 类进行操作, 如果我还有其他的表:
class ArticleCate{ title:string | undefined; desc:string | undefined; status:number | undefined; constructor() { } } // 数据库操作类型 class MysqlDb{ // 添加数据 add(info:ArticleCate):boolean{ console.log(info); return true; } // 删除数据 // 修改数据 // ---- } var art = new ArticleCate(); art.status = 1; art.desc = "国内新闻"; art.title = "国内"; var Db = new MysqlDb(); Db.add(art); // 把这个ArticleCate添加到数据库中
是不是这样就出现了代码的冗余, 有多个要操作的实体类,就要定义多个 MysqlDb 去实现
来看看泛型如何实现, 把 MysqlDb 就封装一次
// 映射数据库字段的实体类 class User{ public username:string | undefined; public pasword:string | undefined; constructor() { } } class ArticleCate{ title:string | undefined; desc:string | undefined; status:number | undefined; constructor() { } } var art = new ArticleCate(); art.status = 1; art.desc = "国内新闻"; art.title = "国内"; var DbArt = new MysqlDb<ArticleCate>(); // 直接实例化 mysqlDb DbArt.add(art); // 把这个ArticleCate添加到数据库中 var user = new User(); user.username = "张三"; user.pasword = "24123"; var DbUser = new MysqlDb<User>(); DbUser.add(user); // 把这个User添加到数据库中 // DbUser.add(123); // 错误写法, 有类型校验
所以泛型, 不仅可以代表基本数据类型, 也可以是定义的类
泛型接口
先来看一个函数接口
// 函数接口 interface ConfigFn{ (val: string, val2: string):string; } // 实现接口 var getData:ConfigFn = function(val1:string, val2:string):string{ return val1 + val2; } var str:string = getData("张三", "李四"); console.log(str);
再来看看泛型接口
// 泛型函数接口 interface ConfigFn{ <T>(val: T, val2: T):T; } // 实现泛型函数接口 var getData:ConfigFn = function<T>(val1:T, val2:T):T{ return val1; } var str:string = getData<string>("张三", "李四"); console.log(str);
实现泛型接口的时候, 不指定泛型类型, 到调用的时候再指定类型
另外一种写法
// 泛型函数接口的另外一种写法 interface ConfigFn<T> { (val: T, val2: T):T; } // 定义函数 function getData<T>(val1:T, val2:T):T{ return val1; } // 实现接口, 并且指定类型 var myGetData:ConfigFn<string> = getData; myGetData("张三", "李四");
注意: 实现泛型接口,本身也要是一个泛型
有了泛型, 调用方法的时候,同样的代码,可供多个类型使用,大大的扩展了代码的复用性