5.泛型

## 泛型的定义 **泛型**:在软件工程中,我们不仅要创建一致的定义良好的API,同时也要考虑泛用性,组件不仅能够支持当前的数据类型,同时也能支持未来的的类型。这在创建大型系统时为你提供了十分灵活的功能。 在像C#和Java这样的语言,可以用泛型创建重要的组件,一个组件可以支持多种类型的数据。这样用户可以以自己的数据类型来使用组件。 通俗理解,泛型是解决 类、接口、方法的复用性、以及对不特定数据类型的支持。 泛型可以帮我们避免重复的代码以及对不特定数据类型的支持(数据校验), ## 泛型的函数 ``` function identity<T>(arg: T): T {//要求传入参数和返回参数类型一致。 return arg; } ``` `T`表示泛型,具体是什么类型由调用方法决定。 ## 泛型接口 ``` interface GenericIdentityFn { <T>(arg: T): T; } function identity<T>(arg: T): T { return arg; } let myIdentity: GenericIdentityFn = identity; ``` 一个相似的例子,我们可能想把泛型参数当作整个接口的一个参数。 这样我们就能清楚的知道使用的++具体是哪个泛型类型++(比如: Dictionary<string>而不只是Dictionary)。 这样接口里的其它成员也能知道这个参数的类型了。 ``` interface GenericIdentityFn<T> { (arg: T): T; } function identity<T>(arg: T): T { return arg; } let myIdentity: GenericIdentityFn<number> = identity; ``` 注意,我们的示例做了少许改动。 不再描述泛型函数,而是把非泛型函数签名作为泛型类型一部分。 当我们使用 GenericIdentityFn的时候,还得传入一个类型参数来指定泛型类型(这里是:number),锁定了之后代码里使用的类型。 对于描述哪部分类型属于泛型部分来说,理解何时把参数放在调用签名里和何时放在接口上是很有帮助的。 ## 泛型类 ``` class GenericNumber<T> { zeroValue: T; add: (x: T, y: T) => T; } let myGenericNumber = new GenericNumber<number>(); myGenericNumber.zeroValue = 0; myGenericNumber.add = function(x, y) { return x + y; }; ``` ``` let stringNumeric = new GenericNumber<string>(); stringNumeric.zeroValue = ""; stringNumeric.add = function(x, y) { return x + y; }; console.log(stringNumeric.add(stringNumeric.zeroValue, "test")); ``` ## 补充 - <T> or <other> 约束参数