TypeScript学习: 九、TypeScript的泛型

泛型的定义

 泛型, 软件开发过程中,我们不仅要创建一致的定义良好的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("张三", "李四");

注意: 实现泛型接口,本身也要是一个泛型

 

 

有了泛型, 调用方法的时候,同样的代码,可供多个类型使用,大大的扩展了代码的复用性 

 

上一篇:【我的技术栈】新blog,新的开始


下一篇:2. TypeScript 基础类型