简单使用
// 泛型约束
// 约束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
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
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
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
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
2
3
4
5
接口泛型定义函数
interface GetArray {
<T>(arg:T, times:number) => T[]
}
1
2
3
2
3
// 这里将泛型变量提到最外层,则接口里都可以使用该泛型变量
interface GetArray<T> {
(arg:T, times:number) => T[],
arr:T[]
}
1
2
3
4
5
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
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
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
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
2
3
4
5
6
7
// 希望同时返回string类型和number类型
// any可以解决这个问题,但是any相当于放弃了类型检查,传入number,可以返回number,也可以返回其它类型
function getData1(value:any):any {
return value;
}
1
2
3
4
5
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
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
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
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
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
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
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
2
3
4
5
6
7
8
9
10
11
12
13
14
深入学习泛型类
泛型:可以帮助我们避免重复的代码以及对不特定数据类型的支持(类型校验)。
把类当做参数的泛型类
- 定义一个类
- 把类作为参数来约束数据传入的类型
需求:定义一个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
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
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
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
自定义操作数据库的库
// 数据库接口
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
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