软件工程中,我们不仅要创建一致的定义良好的 API,同时也要考虑可重用性。 组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。
在像 C#和 Java 这样的语言中,可以使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据。 这样用户就可以以自己的数据类型来使用组件。
通俗理解:泛型就是解决 类 接口 方法的复用性、以及对不特定数据类型的支持(类型校验)。
泛型函数
下面来创建第一个使用泛型的例子:identity 函数。 这个函数会返回任何传入它的值。 你可以把这个函数当成是 echo 命令。
不用泛型的话,这个函数可能是下面这样:
1 2 3 4 5
| function identity(value: string): string { return value; } identity("test");
|
如果我们要同时返回 number 和 string 类型,可以使用 any 解决这个问题
1 2 3 4 5 6
| function identity(value: any): any { return value; } identity("test"); identity(123);
|
使用 any 类型会导致这个函数可以接收任何类型的 arg 参数,这样就丢失了一些信息:传入的类型与返回的类型应该是相同的。如果我们传入一个数字,我们只知道任何类型的值都有可能被返回。这就导致传入和返回的参数类型可能不一致
1 2 3 4 5
| function identity(value: any): any { return "呵呵呵"; } identity(123);
|
因此,我们需要一种方法使返回值的类型与传入参数的类型是相同的。 这里,我们使用了 类型变量,它是一种特殊的变量,只用于表示类型而不是值。
1 2 3
| function identity<T>(arg: T): T { return arg; }
|
我们给 identity 添加了类型变量 T。 T 帮助我们捕获用户传入的类型(比如:number),之后我们就可以使用这个类型。 之后我们再次使用了 T 当做返回值类型。现在我们可以知道参数类型与返回值类型是相同的了。 这允许我们跟踪函数里使用的类型的信息。
T 表示泛型,具体什么类型是调用这个方法的时候决定的。
1 2 3 4 5 6 7 8 9 10 11
| function identity<T>(arg: T): T { return arg; } console.log(identity<number>(123)); console.log(identity<string>("123")); console.log(identity<boolean>(true)); console.log(identity<object>({ age: 123 })); console.log(identity<Array<number>>([1, 2, 3, 4]));
console.log(identity([1, 2, 3, 4]));
|
泛型接口
首先我先回顾一下函数类型接口的定义:
1 2 3 4 5 6 7 8 9
| interface ConfigFn { (value1: string, value2: string): string; }
var setData: ConfigFn = function (value1: string, value2: string): string { return value1 + value2; };
console.log(setData("名字是", "张三"));
|
上面的列子参数我们只能传入 string 类型,如果需要传入其他类型,就需要用到泛型接口了。
1 2 3 4 5 6 7 8 9 10 11
| interface ConfigFn { <T>(value: T): T; }
var setData: ConfigFn = function <T>(value: T): T { return value; };
console.log(setData("张三")); console.log(setData(111));
|
下面我们换一种写法:
1 2 3 4 5 6 7 8 9 10 11 12 13
| interface ConfigFn<T> { (value: T): T; }
function getData<T>(value: T): T { return value; }
var setData: ConfigFn<string> = getData; console.log(setData("张三"));
var setData2: ConfigFn<number> = getData; console.log(setData2(111));
|
泛型类
泛型类使用( <>)括起泛型类型,跟在类名后面。
基本用法
假如有个最小堆算法,需要同时支持返回数字和字符串 a - z 两种类型。通过类的泛型来实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| class MinFun<T> { public list: T[] = []; add(value: T): void { this.list.push(value); } min(): T { let minNum = this.list[0]; this.list.forEach((item) => { if (item < minNum) { minNum = item; } }); return minNum; } }
var m1 = new MinFun<number>(); m1.add(11); m1.add(9); m1.add(5); m1.add(4); console.log(m1.min());
var m2 = new MinFun<string>(); m2.add("d"); m2.add("f"); m2.add("z"); m2.add("b"); console.log(m2.min());
|
具体使用
定义一个 User 的类这个类的作用就是映射数据库字段。
然后定义一个 MysqlDb 的类这个类用于操作数据库。
然后把 User 类作为参数传入到 MysqlDb 中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class User { username: string | undefined; password: string | undefined; }
class MysqlDb<T> { add(info: T): boolean { console.log(info); return true; } } var u = new User(); u.username = "张三"; u.password = "123456";
var Db = new MysqlDb<User>(); Db.add(u);
|
下面我们增加一个 ArticleCate 类和数据库进行映射:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| class User { username: string | undefined; password: string | undefined; }
class MysqlDb<T> { add(info: T): boolean { console.log(info); return true; } updated(info: T, id: number): boolean { console.log(info); console.log(id); return true; } } var u = new User(); u.username = "张三"; u.password = "123456";
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 = params.title; this.desc = params.desc; this.status = params.status; } }
var a = new ArticleCate({ title: "分类", desc: "1111", status: 1, });
var Db2 = new MysqlDb<ArticleCate>(); Db2.add(a);
var b = new ArticleCate({ title: "分类1", desc: "描述", });
a.status = 0; Db2.updated(b, 2);
|
总结一下,这里主要体现把类作为参数类型的泛型类的用法。