1. 很多时候我们希望一个函数或者一个类可以支持多种数据类型,有很大的灵活性
2.泛型:不预先确定的数据类型,具体的类型在使用的时候才能确定
3.泛型好处
a. 函数和类可以支持多种类型,增强程序的扩展性;
b. 不必写多条函数重载,冗余的联合类型声明,增强代码可读性;
c. 灵活的控制了类型之间的约束;
一 泛型函数与泛型接口
// 一个打印函数
function log(value: 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[]) {
console.log(value);
return value;
}
// 希望log可以接收任意类型
// any类型(问题:丢失了类型之间的关系,它忽略了输入的类型和返回的类型必须是一致的)
function log(value: any) {
console.log(value);
return value;
}
使用泛型改造log函数
// 泛型函数
// 类型T不需要预先指定,就相当于any类型
// 可以保存输入参数和返回值类型是一致的
function log<T>(value: T): T {
console.log(value);
return value;
}
// 两种调用方式
log<string[]>(['1','2']);
// 利用TS的类型推断,省略类型的参数
//推荐这种方式
log(['1','2']);
// 利用泛型定义一个函数类型
// type 类型别名
type Log = <T>(value: T) => T;
// 泛型函数的实现
let myLog: Log = log
// 泛型接口 (和类型别名的定义是完全等价的)
// 泛型紧紧约束了一个函数,也可以约束接口的其它成员
interface Log1 {
<T>(value: T): T
}
// 这样接口的所有成员都受到了泛型的约束了
// 当泛型约束了整个接口之后,在实现的时候必须指定一个类型
interface Log2<T> {
(value: T): T
}
// 必须指定类型,mylog2的参数只能是number
let myLog2: Log2<number> = log
// 设置默认值了,实现的时候就不需要必须指定一个类型了
interface Log2<T = string> {
(value: T): T
}
let myLog3: Log2 = log
log('2')
二 泛型类与泛型约束
// 定义一个泛型类(<T>放在Log后面,约束类的所有成员)
// 泛型不能应用于类的静态成员
class Log<T> {
run(value: T) {
console.log(value);
return value;
}
}
// 实例化Log类,实例的方法将会受到泛型的约束
let log = new Log<number>()
log.run(1)
// 实例化的时候也可以不传入参数,不指定参数的时候value就可以是任意值
let log1 = new Log()
log1.run({a:1})
// 泛型约束,
// 希望打印出参数和参数的属性
interface LogLength {
length: number;
}
function log3<T extends LogLength>(value: T): T {
// 不存在length属性,需要预定义一个接口,T继承LogLength就好使了
// 现在T就受到了一定的约束,就不是任意类型都可以传入了,输入的参数必须具有length属性
console.log(value, value.length);
return value;
}
log3([1])
网友评论