泛型
泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
初识泛型
泛型只在编译阶段有效。主要使用方式:泛型类,泛型接口,泛型方法。
首先来看一个例子,创建10个汤姆:
前端优选
function createToms(length:number,name: any): Array<any>{
let result = [];
for(let i=0;i<length;i++){
result[i] = name;
}
return result;
}
console.log(createToms(10,'tom')); //['tom', 'tom', 'tom','tom', 'tom', 'tom','tom', 'tom', 'tom','tom']
该方法创建一个指定长度的数组,代码编译的时候不会报错,但是有一个显而易见的缺点就是:它并没有规定返回值类型。Array允许填充任意类型的值,但是我们期望数组的每一项都是name的类型。
泛型登场
function createToms<T>(length:number,name: T): Array<T>{
let result = [];
for(let i=0;i<length;i++){
result[i] = name;
}
return result;
}
console.log(createToms<string>(10,'tom')); //['tom', 'tom', 'tom','tom', 'tom', 'tom','tom', 'tom', 'tom','tom']
函数名后面添加了,T可以用来代替任意输入的类型,在后面输入的T和输出的Array即可使用了。
这里T代表Type,定义泛型时一般将第一个类型变量的名称用作泛型代表。实际上T可以用任何有效字母来代替,一般情况下,K(Key)表示键类型;V(Value)表示值类型;E(Element)表示元素类型。
多个泛型
还是同样的方法,但是我有可能创建的不是汤姆了:
前端优选
function createToms<T,U>(length: T,name: U):Array<T>{
let result = [];
for(let i=0;i < Number(length);i++){
result[i] = name;
}
return result;
}
console.log(createToms<number,string>(10,'tom')); //['tom', 'tom', 'tom','tom', 'tom', 'tom','tom', 'tom', 'tom','tom']
console.log(createToms<number,number>(10,86)); //[86, 86, 86, 86,86, 86, 86, 86,86, 86]
在调用的时候指定具体类型为tom或者86,当然,如果不指定也可以根据参数推定出来。
多个类型参数
定义泛型的时候,可以一次定义多个类型参数
function createTom<T,U>(tuple: [T,U]): [T,U]{
return [tuple[0],tuple[1]];
}
console.log(createTom([18,'Tom'])); //[ 18, 'Tom' ]
function createTom<T,U>(tuple: [T,U]): [U,T]{
return [tuple[1],tuple[0]];
}
console.log(createTom([18,'Tom'])); //[ 'Tom', 18 ]
上例中,在返回值位置取值tuple值是相反的,这样写的目的希望您更能容易理解元组(元组:合并了不同类型的对象)取值方式。同时,上例也表现了定义泛型时,一次定义多个类型参数。
泛型约束
泛型约束的目的是保证要使用的属性或方法存在:
interface lengthType {
len: number;
}
function createToms<T extends lengthType,U>(length: T,name: U):Array<T>{
let result = [];
for(let i=0;i < length.len;i++){ //保证运算符<应用于Number类型参数
result[i] = name;
}
return result;
}
let res = createToms<lengthType,string>({len:3},'tom'); //[ 'tom', 'tom', 'tom' ]
这里限制参数类型必须满足lengthType
接口,因此T处应放置lengthType
。
有了泛型之后,一个函数或容器类能处理的类型一下子扩到了无限大,似乎有点失控的感觉。因此又产生了多个类型参数之间可以互相约束的概念。
function copyCousin<T extends U,U>(target: T,source: U): T{
for(let k in source){
target[k] = (<T>source)[k];
}
return target;
}
let jerry = {a:1,b:2,c:3,d:4};
console.log(copyCousin(jerry,{b:20,c:30})) //{ a: 1, b: 20, c: 30, d: 4 }
使用了两个类型参数,其中要求T继承自U,这样就保证了U上不会出现T没有的字段。
杰瑞继承自表哥的衣服属性,即可获得相应的能力去震慑汤姆。
前端优选
泛型接口
上例的createToms方法中通过接口限制了参数类型,当然也可以通过泛型来直接限制接口:
interface createTom{
<T>(length: number,name: T) : Array<T>
}
let Tom: createTom;
Tom = function<T>(length:number,name: T): Array<T>{
let result: T[] = [];
for(let i=0;i<length;i++){
result[i] = name;
}
return result;
}
console.log(Tom(3,'tom')); //[ 'tom', 'tom', 'tom' ]
前端优选
泛型类
与泛型接口类似,泛型还可以用于定义类中:
class createTom<T>{
catching:(x:T,y:T) => T;
}
let tom = new createTom<string>();
tom.catching = (x,y) => {return x + y}
console.log(tom.catching('Tom',' is catching Jerry.')); //Tom is catching Jerry.
简单来说,就是可以用来限制方法参数类型。
前端优选
本文简单介绍了TypeScript泛型的基本用法,泛型在编码过程中是非常重要的一个环节,希望本文能帮助到大家,欢迎大家继续关注后续内容哦~