泛型
1、什么是泛型
当需要一个函数或类支持多种数据类型时,具有很高的灵活性,此时泛型就出现了。
泛型的含义为:不预先确定函数类型,具体的类型在使用时才确认。
function log(value:string):string { // 目前只接受字符串类型的参数
console.log(value);
return value;
}
// 如果接受一个字符串数组,可通过函数重载实现
function log (value: string): string;
function log (value: string[]): string [];
function log (value: any) {
console.log(value);
return value;
}
// 联合类型也可实现
function log (value: string | string []) :string | string [] {
console.log(value);
return value;
}
// 如果接受任何类型的参数该如何处理
// 方法: any
function log (value: any) { // 但是此方法忽略了输入的类型和返回的类型必须是一致的这种关系
console.log(value);
return value;
}
// 使用泛型实现
function log<T>(value: T): T { // 类型 T 不需要预先的指定,可保证输入参数类型和输出是一致的
console.log(T);
return T;
}
// 使用时,可直接指定数据的类型
log<string[]>(['a','b']);
log(['a','b']); // 利用 ts 的类型推断,不指定数据类型
2、泛型函数与泛型接口
2.1 泛型函数
泛型可以定义函数,也可以定义函数类型;
// 泛型函数的实现
type Log = <T>(value: T): T;
let mylog :Log = log;
2.2 泛型接口
interface Log {
<T>(value: T): T // 只约束方法
}
interface Log<T> { // 约束所有成员类型
(value: T): T
}
3、泛型类与泛型约束
3.1 泛型类
泛型可以约束类的成员
class Log<T> { // 将泛型类放在类名的后面,可以约束所有的成员
run(value: T) {
console.log(value)
return value
}
}
需要注意的是,泛型不能用于类的静态成员。如果此时 run 的修饰符为 static,那么编译器会报错:静态类型不能引用类类型参数。根据以上,实例化一个方法:
let log1 = new Log<number>(); // 实例化的时候,需要指定泛型的类型
log1.run(1); // 此时入参只能是 number 类型
let log2 = new Log(); // 如果不指定类型,那么成员入参可是任意类型
log2.run({});
3.2 泛型约束
改造一下 log 方法:
function log<T>(value: T): T {
console.log(value, value.length); // 打印 value 的属性,此时会报错, T 类型上不存在 length 属性
return value;
}
此时需要使用类型约束。定义一个接口,然后让 T 继承字这个接口:
interface Length {
length: number
}
function log<T extends Length>(value: T): T { // 此时,T 收到类型约束,不是所有类型都可以传入,输入的参数不管是什么类型,但是必须具有 length 属性
console.log(value, value.length);
return value;
}
// 只要入参带有 length 属性即可
log([1])
log('123')
log({ length: 3 })
总结,泛型的优点在于:
- 函数和类可以轻松的支持多种类型,增强程序的扩展性
- 不必写多条函数重载,冗长的联合类型声明,增强代码可读性
- 灵活控制类型之间的约束
网友评论