TypeScript:泛型
什么是泛型
泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
1 函数泛型
//<T>表示定义模板类型,(arg: T): T 表示参数和返回值类型都是同一个类型, //具体T是什么类型就实参决定 function a<T>(arg: T): T { return arg; } let r1 = a(100);//T的类型是number let r2 = a("小王");//T的类型是string console.log(r1, r2);
匿名函数和箭头函数泛型语法
//匿名函数 let a = function <T>(arg: T): T { return arg; } //箭头函数 let a = <T>(arg: T):T =>{ return arg; }
我们来实现一个函数 createArray,它可以创建一个指定长度的数组,同时将每一项都填充一个默认值:
function createArray<T123>(len: number, value: T123): T123[] { let arr: any[] = []; for (let j: number = 0; j < len; j++) { arr.push(value); } return arr; } console.log(createArray<string>(5, 'h')); console.log(createArray(5, 0o700)); interface dataType { id: number; title: string; cover: string; tags: string[] } console.log(createArray<dataType>(5, { id: 23, title: '今天星期天', cover: 'http:///7001.png', tags: ['创意', '星期天', '线上'] }));
2 模板类型可以是多个
function a<T1, T2>(a:string, b:T1, c:T2): T2 { return c; } a("纟", 100, true) function a<T1, T2>(a:string, b:T1, c:T2): string { return "小王"; } let a = function<T1, T2>(a:string, b:T1, c:T2): T2 { return c; } let a = <T1, T2>(a:string, b:T1, c:T2): T2 => { return c; } function change<T1, T2>(tuple: [T1, T2]): [T2, T1] { return [tuple[1], tuple[0]]; }
3 泛型的错误
function a(a:T1, b:T2): T1 { let r = a + b;//编译报错,因为编译器不知道T1和T2是什么类型,所以不确定是否能进行 + 运算 return r; } let r = a("小王", 100); console.log(r); function a(a:T1): T1 { return a+100;//编译报错,因为编译器不知道T1是什么类型,所以不确定是否能进行 + 100 } function loggingIdentity(arg: T): T { console.log(arg.length) //编译报错,因为编译器不知道T1是什么类型,所以不确定是否有属性length return arg }
4 泛型函数变量
函数的变量类型也可以使用泛型
function identity<T>(arg: T): T { return arg; } interface IM { {<x>(arg: x):x} } let x:IM = identity;// let myIdentity1: <U>(arg:U)=>U = identity; let myIdentity2: {<U>(arg:U):U} = identity;
5 泛型函数类型接口
(1)泛型函数类型接口1
function identity<T>(arg: T): T { console.log("identity") return arg; } //泛型函数类型接口1, 其中的T可以改为其它自定我,不必与函数中T相同 interface GenericIdentityFn1 { <T>(arg: T): T; } //值也必须是有泛型的函数 let myIdentity: GenericIdentityFn1 = identity; //<string>决定了参数只能使用string myIdentity<string>("sddssd"); //<number>决定了参数只能使用number myIdentity<number>(234);
(2)泛型类函数型接口2
//泛型类函数型接口2 interface GenericIdentityFn2<T> { (arg: T): T; } //<number>决定了参数只能使用number let myIdentity2: GenericIdentityFn2<number> = identity; myIdentity2(234); //<string>决定了参数只能使用string let myIdentity3: GenericIdentityFn2<string> = identity; myIdentity3("小王"); console.log(r);
6 泛型类1
(1)新new出对象是{ },其中无成员
(2)对象中只能全部或部分实现类中声明好的成员,不能增加其它成员
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; }; // 或 yGenericNumber.add = function(x:number, y:number) { return x + y; }; let r = myGenericNumber.add(1, 22); console.log(r); //myGenericNumber.sex = ‘男’; 错,因为类中没有声明sex let stringNumeric = new GenericNumber<string>(); stringNumeric.zeroValue = "fds"; stringNumeric.add = function(x, y) { return x + y; }; let r2 = stringNumeric.add("1", "22"); console.log(r2);
7 泛型类2
new出的对象已经具体类中的成员
class A<T> { a:T; show(a:T, b:T) { console.log(a, b); } } let a1 = new A<string>(); a1.show("fsd","fsd") let a2 = new A<number>(); a2.show(123,444) //下面代码也不会报错,T不生效 let a3 = new A(); a3.show(444,"555");
8 泛型约束
在函数内部使用泛型变量的时候,由于事先不知道它是哪种类型,所以不能随意的操作它的属性或方法.
<T extends IA> 要求T类型数据至少有IA中声明的成员
interface IA { length: number; } //要求T类型数据有length属性 function m1<T extends IA>(arg: T): T { console.log(arg.length); return arg; } let r; r = m1("小王");//"小王"有length属性 console.log(r);//2 r = m1({length: 10, value: 3});//{length: 10, value: 3}有length属性 console.log(r);//10 interface IA { length: number; value: number; } //要求T有属性length和value function m1<T extends IA>(arg: T): T { console.log(arg.length, arg.value); return arg; } m1({length: 10, value: 3, age:30});
以上泛型约束示例仅仅是函数中使用,实计上也可以使用于其它使用泛型的地方,看看下面的示例吧
class Person { name:string; sex:string; constructor(name, sex) { this.name = name; this.sex = sex; } show() { console.log(this.name, this.sex); } } class Stu<T> { show(p:T) { console.log(p); p.show();//有错 } } let stu = new Stu<Person>(); let p = new Person("小王", '男'); stu.show(p);
以下示例有一个问题,p.show()会出错, 如果改为下面的泛型约束就可以解决该类问题
class Person { name:string; sex:string; constructor(name, sex) { this.name = name; this.sex = sex; } show() { console.log(this.name, this.sex); } } class Stu<T extends Person> { show(p:T) { console.log(p); p.show(); } } let stu = new Stu<Person>(); stu.show() let p = new Person("小王", '男'); stu.show(p);
多个类型参数之间也可以互相约束:
function copyObj<T extends U, U>(target: T, source: U): T { for (const k in source) { target[k] = (<T>source)[k]; } return target; } let x123 = {a:1, b:2, c:3}; const source1 = {a:1, b:2, c:3}; copyObj(x123, source1); console.log(x123);
9 泛型参数的默认类型
我们可以为泛型中的类型参数指定默认类型。当使用泛型时没有在代码中直接指定类型参数,从实际值参数中也无法推测出时,这个默认类型就会起作用。
function createArray<T = string>(length: number, value: T): Array<T> { let result: T[] = []; for (let i = 0; i < length; i++) { result[i] = value; } return result; }