泛型就是不预先确定的数据类型,具体的类型在使用的时候才能确定。
我们通常有泛型参数,泛型函数,泛型接口,泛型类等。
泛型参数
泛型参数与函数的参数等同对待,它只不过是另一个维度的参数,是代表类型,而不是代表有值的参数。泛型在高级类型中有广泛的应用。
首先我们来看一个函数:
function log(value: string): string {
console.log(value);
return value;
}
如果打印的value是个数组类型,前面介绍过,我们就需要用函数重载的方法:
function log(value: string): string;
function log(value: string[]): string[];
function log(value: any): any {
console.log(value);
return value;
}
当然也可以用联合类型:
function log(value: string | string[]): string | string[] {
console.log(value);
return value;
}
还有any类型:
function log(value: any): any {
console.log(value);
return value;
}
any类型没有输入参数的约束限制。这里就需要泛型。
我们用泛型来 改造我们的log函数。
function log<T>(value: T): T {
console.log(value);
return value;
}
log<string[]>(['a', 'b']);
log(['a', 'b']); // 这种是比较推荐的方式
泛型函数
不仅可以用泛型来定义函数参数,也可以定义函数类型。下面来看一个泛型函数的实现。
function log<T>(value: T): T {
console.log(value);
return value;
}
type Log = <T>(value: T) => T
let myLog: Log = log;
泛型接口
function log<T>(value: T): T {
console.log(value);
return value;
}
interface Log<T> {
(value: T): T
}
let myLog: Log<number> = log;
myLog(1);
如果不指定类型,我们也可以在接口的定义中指定一个默认的类型。
function log<T>(value: T): T {
console.log(value);
return value;
}
interface Log<T = string> {
(value: T): T
}
let myLog: Log = log;
myLog('1'); // 这里myLog的参数就默认为string类型了。
泛型类
与泛型接口非常类似,泛型也可以约束类的成员, 泛型不能应用于类的静态成员中。
class Log<T> {
run (value: T) {
console.log(value);
return value;
}
}
let log1 = new Log<number>();
log1.run(1);
let log2 = new Log();
log2.run('2');
log2.run(2);
泛型约束
class Log<T> {
run (value: T) {
console.log(value, value.length); // Property 'length' does not exist on type 'T'.
return value;
}
}
这个时候就需要用到类型约束这个概念。
interface Length {
length: number
}
function log<T extends Length>(value: T): T {
console.log(value, value.length); // Property 'length' does not exist on type 'T'.
return value;
}
log([1]);
log('123');
log({length: 1});
这样我们就能通过类型检查。T继承了length接口,这表示T受到了一些的约束,就不再是任意类型都可以传了。输入的参数不管是什么类型,但是必须有length属性。
使用泛型的好处
1、函数和类可以轻松的支持多种类型,增强程序的扩展性。
2、不必写多条函数重载,冗长的联合类型声明,增强代码可读性。
3、灵活控制类型之间的约束。
|
请发表评论