在编程中,定义一个变量不确定类型的时候有两种解决方式:
1.使用any
2.使用泛型
什么是泛型
泛型是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候在指定类型的一直特征。
为什么要使用泛型
因为any会在调试中丢失很多的信息,所以我们不建议使用any,这个时候应该有更好的解决方案,那就是泛型。
在函数中使用泛型
在函数名之后写上fn<泛型名>,通常泛型名使用大写的T,当然你可以随意命名,在调用的时候函数名字后面加上fn<泛型类型>
function fn<T>(arr:any[], n: number):any[] {}
fn<number>(['1','2','3'],2)
泛型类型“T”就像一个参数,可供传递,如我们在调用函数时在尖括号中传入,则这个T的类型就是number,
这样我们就可以any类型都换成泛型就可以了,这样就当泛型参数传入number类型时,函数的返回值就是number类型的数组
function fn<T>(arr:T[], n: number) :T[]{
const newArr: T[] = [];
return newArr
}
const newArr = fn<number>([1, 2, 3, 4, 5, 6], 3)
这里T类型就是number,TS就会识别类型,当然你也可以不传入泛型类型,如下
function fn<T>(arr:T[], n: number) :T[]{
if (n >= arr.length) {
return arr
}
const newArr: T[] = [];
for (let i = 0; i < n; i++) {
newArr.push(arr[i])
}
return newArr
}
const newArr = fn([1, 2, 3, 4, 5, 6], 3)
这时TS会自动识别[1, 2, 3, 4, 5, 6]函数第一个参数你传入的是一个number[]
类型的数组,fn<T>(arr:T[], n: number) :T[]{}
从而推导出arr:T[]
中的T泛型是number类型,这样其他几个T泛型也都是number,这样最终的返回结果newArr也是number[]
类型的数组
这里不知道大家有没有疑问,当如果我不传递T泛型参数时,我的数组是长这样的[1, 2, ‘4’, 5],那它返回的是什么类型?
是any吗?还是说还是一个数组,只不过是里面既有number又有string类型的数组,当然是第二种的只不过这个时候表示的是(string | number)[]
的写法
function fn<T>(arr:T[], n: number) :T[]{
if (n >= arr.length) {
return arr
}
const newArr: T[] = [];
for (let i = 0; i < n; i++) {
newArr.push(arr[i])
}
return newArr
}
const newArr = fn([1, 2, '4', 5, 5, 6], 3)
当然这种是在你不传递T泛型参数的情况下才能这样,如果又泛型参数,就只能规规矩矩的进行了。
使用方式类似于函数传参,传什么数据类型,T就表示什么数据类型, 使用时,T也可以换成任意字符串。
泛型相当于是一个类型变量,在定义时,无法预先知道具体的类型,可以使用该变量来代替,只有到调用时,才能确定它的类型
在接口中使用泛型
//形式一
interface Search {
<T,Y>(name:T,age:Y):T
}
let fn:Search = function <T, Y>(name: T, id:Y):T {
console.log(name, id)
return name;
}
fn('li',11);//编译器会自动识别传入的参数,将传入的参数的类型认为是泛型指定的类型
//形式二
interface Search<T, Y> {
name: T,
age: Y
}
function fn<T, U> (name: T, age: U): Search<T, U> {
console.log(name + ": " + typeof (name));//章三:string
console.log(age + ": " + typeof (age));//28:number
let user: Search<T, U> = {
name,
age
};
return user;
}
console.log(fn('章三', 28));//{name: '章三', age: 28}
在类中使用泛型
class Animal<T> {
name:T;
constructor(name: T){
this.name = name;
}
say<T>(say:T) {
console.log(say)
}
}
let cat = new Animal('cat');
cat.say('mimi')
泛型约束
使用接口约束泛型
interface Animal<U> {
name: U
run: () => U
}
//接口泛型约束类属性
class AnimalClass<T> implements Animal<T> {
name: T
constructor(name: T) {
this.name = name
}
run(): T {
return this.name + '在跑步'
}
}
const animal1 = new AnimalClass<String>('小黄狗');
console.log(animal1.run()); // 小黄狗在跑步
const animal2 = new AnimalClass<Number>(666);
console.log(animal2.run()); // 666在跑步