「这是我参与11月更文挑战的第5天,活动详情查看:2021最后一次更文挑战」。
一、泛型的作用
泛型的作用:
- 使用
泛型来创建可重用的组件,一个组件可以支持多种类型的数据。 - 不必多条函数重载,冗长的联合类型声明,增强代码可读性。
- 灵活控制类型之间的约束。
例如,要求一个方法的返回值的类型与传入参数的类型是相同的,可以使用泛型来定义。
例子:
function print1<T>(params:T):T {
return params;
}
print1<string>('name');
上例中定义了泛型print1,用类型变量T捕获用户传入的类型string,泛型print1中使用到的类型变量T就可以看做是string。不用预先定义类型就可以保持输入参数和返回值类型一致。
二、泛型函数
上例中,我们定义了一个泛型函数,其调用方法除了上面的传入类型参数string外,还有另一种写法说直接不传类型参数,编译器会自动确定类型(类型推论)。
print1('name');
三、泛型函数类型
泛型除了可以定义函数,还可以定义函数类型。
type Print = <T>(value:T) => T;
let myPrint: Print = print1;
上例中用type关键字定义函数类型Print,定义myPrint函数为Print类型。
四、泛型接口
泛型还可定义接口:
interface Log<T> {
(value: T):T
}
let myLog:Log<string> = print1;
myLog('aaa')
上例中定义了函数类型的泛型接口Log,指定类型参数为T,设置接口Log中接收参数和返回值都为T类型。
可为类型参数T设置默认值:
interface Log<T=string> {
(value: T):T
}
当有默认值时,使用泛型时就可以不传入类型:
let myLog:Log = print1;
myLog('aaa')
五、泛型类
泛型类与泛型接口差不多。类名后加参数类型。
class Log<T> {
run(value:T):T {
return value;
}
}
上例中创建了泛型类Log,类中定义run方法的参数和返回值都为T类型。
类使用时可设置类型,也可不设置类型,不设置类型时可随意传值:
let log1 = new Log<number>();
log1.run(1);
let log2 = new Log();
log2.run('sss');
六、泛型约束
泛型约束既是对类型变量T的类型约束。
举个例子:
function getLength<T> (arg: T) {
console.log(arg.length);//报错
return arg
}
getLength<string>('22')
上例中,我们要打印参数的length属性,但是参数不一定有length属性,此时我们就需要对传入的参数联系进行约束,否则会报错。
那如何对参数进行约束呢?
类型变量T可以继承一个接口进行泛型约束,如下所示:
interface Length {
length:number;
}
function getLength<T extends Length> (arg: T) {
console.log(arg.length)
return arg
}
getLength<string>('22')
getLength('33')
上例中,类型变量T继承Length接口进行泛型约束,因此传入的参数必须拥有length属性才不会报错。