简单使用

// 泛型约束
// 约束value和返回值都遵循泛型约束
const getArray = <T>(value:T, times:number = 5):T[] => {
    return new Array(times).fill(value);
}

// 函数调用的时候传入泛型值
console.log(getArray<string>('6', 6)); // [ '6', '6', '6', '6', '6', '6' ]
1
2
3
4
5
6
7
8

泛型变量

// 这里的T就是泛型变量
const getArray = <T>(value:T, times:number = 5):T[] => {
    return new Array(times).fill(value);
}
1
2
3
4
// 约束返回值是元组类型的数组
const getArray = <T, U>(param1:T, param2:U, times:number):Array<[T, U]> => {
    return new Array(times).fill([param1, param2]);
}

console.log(getArray<number, string>(1, 'a', 3)); // [ [ 1, 'a' ], [ 1, 'a' ], [ 1, 'a' ] ]
1
2
3
4
5
6
// 泛型约束函数
let getArray:<T>(arg:T, times:number) => T[];
getArray = (arg:any, times:number) => {
    return new Array(times).fill(arg);
}

console.log(getArray(123, 3)); // [ 123, 123, 123 ]
1
2
3
4
5
6
7

泛型类型

type GetArray = <T>(arg:T, times:number) => T[];
// 定义函数getArray为GetArray类型
let getArray:GetArray = (arg:any, times:number) => {
    return new Array(times).fill(arg);
}
1
2
3
4
5

接口泛型定义函数

interface GetArray {
    <T>(arg:T, times:number) => T[]
}
1
2
3
// 这里将泛型变量提到最外层,则接口里都可以使用该泛型变量
interface GetArray<T> {
    (arg:T, times:number) => T[],
    arr:T[]
}
1
2
3
4
5

泛型约束

interface ValueWithLength {
    length:number
}
// 泛型变量继承接口ValueWithLength,将会继承接口的属性约束
const getArray = <T extends ValueWithLength>(arg:T, times:number):T[] => {
    return new Array(times).fill(arg);
}

console.log(getArray([1, 2], 3)); // [ [ 1, 2 ], [ 1, 2 ], [ 1, 2 ] ]
console.log(getArray(123, 3)); // 报错,数字123不具备length属性
1
2
3
4
5
6
7
8
9
10

在泛型约束中使用类型参数

// keyof T表示T类型的所有键组成的集合
// 这里约束K必须是T的键,否则报错
const getProps = <T, K extends keyof T>(obj:T, propName:K) => {
    return obj[propName];
}
const objs = {
    name: 'lisi',
    age: 12
}
console.log(getProps(objs, 'name'));
console.log(getProps(objs, 'sex')); // 报错,objs上没有sex属性
1
2
3
4
5
6
7
8
9
10
11

泛型的定义

泛型:软件工程中,我们不仅要创建一致的定义良好的API,同时也要考虑可重用性。组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。

在C#和java这样的语言中,可以使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据。这样用户就可以以自己的数据类型来使用组件。

通俗理解:泛型就是解决类、接口、方法的复用性,以及对不特定数据类型的支持。

泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性

泛型:可以支持不特定的数据类型。要求:传入的参数和返回的参数一致。any放弃了类型检查(传入类型检查和返回类型检查)。

// 只能返回string类型的数据
function getData(value:string):string {
     return value;
}
1
2
3
4
// 希望同时返回string类型和number类型,需要定义两个方法,代码冗余
function getData1(value:string):string {
    return value;
}
function getData2(value:number):number {
    return value;
}
1
2
3
4
5
6
7
// 希望同时返回string类型和number类型
// any可以解决这个问题,但是any相当于放弃了类型检查,传入number,可以返回number,也可以返回其它类型
function getData1(value:any):any {
    return value;
}
1
2
3
4
5
// 泛型
function createArray<T>(length: number, value: T): Array<T> {
    let result: T[] = [];
    for (let i = 0; i < length; i++) {
        result[i] = value;
    }
    return result;
}

console.log(createArray<string>(3, 'a'));
1
2
3
4
5
6
7
8
9
10

泛型函数

// T表示泛型:可以支持不特定的数据类型,具体什么类型是调用这个方法的时候决定的
function getInfo<T>(value:T):T {
    return value;
}
// 这里约定getInfo必须传入number类型且返回也是number类型
console.log(getInfo<number>(111));
// 这里约定getInfo必须传入string类型且返回也是string类型
console.log(getInfo<string>('ceshi'));
1
2
3
4
5
6
7
8

泛型类(有类型校验)

泛型类:比如有一个最小堆算法,需要同时支持返回数字和字符串(a-z)两种类型,通过类的泛型来实现。

// 泛型类
class MinClass {
    public list:number[] = [];
    add(num:number) {
        this.list.push(num);
    }
    min():number {
        // 假设第一个元素是最小
        let minNum = this.list[0];
        for (let i = 1; i < this.list.length; i++) {
            if (minNum > this.list[i]) {
                minNum = this.list[i];
            }
        }
        return minNum;
    }
}

let min = new MinClass();
min.add(2);
min.add(4);
min.add(5);
min.min(5); // 2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 泛型类
class MinClass<T> {
    public list:T[] = [];
    add(num:T):void {
        this.list.push(num);
    }
    min():T {
        let minNum = this.list[0];
        for (let i = 1; i < this.list.length; i++) {
            if (minNum > this.list[i]) {
                minNum = this.list[i];
            }
        }
        return minNum;
    }
}
// 实例化类并且指定了类的泛型为number
// let min = new MinClass<number>();
// min.add(1);
// min.add(4);
// min.add(5);
// console.log(min.min()); // 1

// 实例化类并且指定了类的泛型为string
let min = new MinClass<string>();
min.add('a');
min.add('c');
min.add('d');
console.log(min.min()); // a
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

泛型接口

// 函数类型接口:约束函数的两个参数和返回值都必须是字符串类型
interface ConfigFn {
    (value1:string, value2:string):string;
}

let setData:ConfigFn = function(value1:string, value2:string):string {
    return value1 + value2;
}
console.log(setData('1', 'a'));
1
2
3
4
5
6
7
8
9

上述接口,如果想实现传入数字并返回数字是无法实现的。这就需要借助泛型接口来实现。

// 方法1
// 泛型接口约束类型是函数调用的时候才能确定
interface ConfigFn {
    <T>(value1:T):T;
}

let getData:ConfigFn = function<T>(value:T):T {
    return value;
}

console.log(getData<string>('a')); // a
console.log(getData<number>(123)); // 123
1
2
3
4
5
6
7
8
9
10
11
12
// 方法2
interface ConfigFn<T> {
    (value1:T):T;
}

function getData<T>(value:T) {
    return value;
}

let myGetData:ConfigFn<string> = getData;
let myGetData2:ConfigFn<number> = getData;

console.log(myGetData('aaa')); // aaa
console.log(myGetData2(123)); // 123
1
2
3
4
5
6
7
8
9
10
11
12
13
14

深入学习泛型类

泛型:可以帮助我们避免重复的代码以及对不特定数据类型的支持(类型校验)。

把类当做参数的泛型类

  1. 定义一个类
  2. 把类作为参数来约束数据传入的类型

需求:定义一个User类,这个类的作用就是映射数据库字段,然后定义一个MysqlDb的类,这个类用于操作数据库,然后把User类作为参数传入到MysqlDb中。

class User {
    username:string | undefined;
    password:string | undefined;
    constructor(username:string | undefined, password:string | undefined) {
        this.username = username;
        this.password = password;
    }
}

class MysqlDb {
    // 把类作为参数来约束数据传入的类型
    add(user:User):boolean {
        console.log(user); // User {username: "lisi", password: "111"}
        return true;
    }
}
// 创建一个user实例
const user = new User('lisi', '111');
// 创建一个MysqlDb实例
const db = new MysqlDb();

// 调用MysqlDb实例的add方法将user实例添加到数据库中
console.log(db.add(user));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class ArticleCate {
    title:string | undefined;
    desc:string | undefined;
    status: number | undefined;
    constructor(username:string | undefined, password:string | undefined, status: number | undefined) {
        this.title = username;
        this.desc = password;
        this.status = status;
    }
}

class MysqlDb {
    // 把类作为参数来约束数据传入的类型
    add(info:ArticleCate):boolean {
        console.log(info); // ArticleCate {title: "js", desc: "入门", status: 1}
        return true;
    }
}

let artical = new ArticleCate('js', '入门', 1);
let db = new MysqlDb();

console.log(db.add(artical));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

封装数据库的泛型类

封装数据库的泛型类:减少代码冗余。

// 操作数据库的泛型类
class MysqlDb<T> {
    // 把类作为参数来约束数据传入的类型
    add(info:T):boolean {
        console.log(info);
        return true;
    }
    update(info:T, id:number):boolean {
        console.log(info);
        console.log(id);
        return true;
    }
}

// 想给User表增加数据
// 定义一个User类和数据库进行映射
class User {
    username:string | undefined;
    password:string | undefined;
    constructor(username:string | undefined, password:string | undefined) {
        this.username = username;
        this.password = password;
    }
}

class ArticleCate {
    title:string | undefined;
    desc:string | undefined;
    status: number | undefined;
    constructor(username:string | undefined, password:string | undefined, status: number | undefined) {
        this.title = username;
        this.desc = password;
        this.status = status;
    }
}

const user = new User('lisi', '111');
const db1 = new MysqlDb<User>();

console.log(db1.add(user));

const artical = new ArticleCate('js', '入门', 12);
const db2 = new MysqlDb<ArticleCate>();
// 只能添加ArticleCate类的数据
console.log(db2.add(artical));
// 更新数据
artical.status = 20;
console.log(db2.update(artical, 100));
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

自定义操作数据库的库

e5f7df29bfaa6cf3a200ec65a4052ff2.png

// 数据库接口
interface DB<T> {
    get(id:number):any[];
    add(info:T):boolean;
    update(info:T, id:number):boolean;
    delete(id:number):boolean;
}
// 定义一个操作mysql数据库的类
// 注意:要实现泛型接口,这个类也应该是一个泛型类
class MysqlDB<T> implements DB<T> {
    constructor() {
        // 构造函数里实现与数据库建立连接
    }

    get(id: number): any[] {
        throw new Error("Method not implemented.");
    }
    add(info: T): boolean {
        console.log(info);
        // return true;
        throw new Error("Method not implemented.");
    }
    update(info: T, id: number): boolean {
        throw new Error("Method not implemented.");
    }
    delete(id: number): boolean {
        throw new Error("Method not implemented.");
    }
}

// 定义一个操作mssql数据库的类
class MsSqlDB<T> implements DB<T> {
    get(id: number): any[] {
        throw new Error("Method not implemented.");
    }
    add(info: T): boolean {
        console.log(info);
        // return true;
        throw new Error("Method not implemented.");
    }
    update(info: T, id: number): boolean {
        throw new Error("Method not implemented.");
    }
    delete(id: number): boolean {
        throw new Error("Method not implemented.");
    }
}

// 操作用户表,定义一个User类和数据表做映射
class User {
    username:string | undefined;
    password:string | undefined;
    constructor(username:string | undefined, password:string | undefined) {
        this.username = username;
        this.password = password;
    }
}

let user = new User('lisi123', '123');
let oMysql = new MysqlDB<User>(); // User类作为参数来约束数据传入的类型
oMysql.add(user);

let oMssql = new MsSqlDB<User>(); // User类作为参数来约束数据传入的类型
oMssql.add(user);
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
59
60
61
62
63
64