ts语法---泛型和泛型约束

I_am_shy 2024-10-03 15:03:03 阅读 62

泛型

泛型,动态类型,是一个初始化不明确的类型,类似于函数中的形参(不明确参数值),

泛型一般用在function定义函数时动态约束类型,和type定义类型时动态约束类型,

泛型一般使用任意的大写字母规定,同名的泛型表示同一种类型

泛型在实例化使用后(定义了明确类型之后,本次实例化使用就只能表示这种类型)

泛型的使用示例

<code>// 将参数合成数组

function add<T>(a:T,b:T):Array<T>{

return [a,b];

}

add(1,2) //这里的T被固定成number

add('a','b') //这里的T被固定成string

add(true,false)// 这里的T被固定成boolean

这里实现了一个数组合成的函数,它接受两个同类型的参数,返回这个类型的数组,

在这个案例中,接受了参数类型并不明确,只是要求两个参数的类型和返回值的类型要一致,至于具体是什么类型并没有强制要求,

于是就可以设置一个泛型T来规定,这里T表示一种类型,在使用函数设置了参数后,T就被固定类型了,此时整个函数中所有的T都时同一种类型,例如上面的add(1,2),这个add里面的T就被固定(替换)成了number类型,

泛型的特点

1. 泛型可以指定多个,如<T,U>

2. 泛型可以指定默认类型,如<T=number>

3. 泛型可以指定联合类型

 

type Ab<T,U=object> = number | string | T | U;

let ab:Ab<boolean>;

ab =20;

ab ='a';

ab =true;

ab = {};

以上ab变量可以被设置成number,string,object,和Boolean类型中的任意一种,

Ab类型中U泛型有默认值,不设置的情况下默认为object,而T没有默认值,必须设置类型

使用泛型封装模拟axios的get方法

有了泛型,对于一些未知类型数据的操作就方便很多,泛型最常用的地方就是ajax网络请求,网络请求一般是会用来请求多种数据的,要标记出每种数据(联合所有可能的类型)不太容易,这个时候就可以使用泛型,当请求结果出来后,泛型被固定,就可以获得类型检测了

// 封装模拟axios的get方法, 返回一个Promise,Promise成功时返回请求结果

const axios = {

get<T>(url:string):Promise<T>{

return new Promise((resolve,reject)=>{

let xhr:XMLHttpRequest = new XMLHttpRequest();

xhr.open('GET',url);

xhr.onload = ()=>{

if(xhr.status >= 200 && xhr.status < 300){

resolve(JSON.parse(xhr.response));

}

}

xhr.send();

})

}

}

interface Data{//指定返回数据的类型,否则对于不明确的类型,无法固定泛型,默认会定为unknow

code:number,

msg:string

}

axios.get<Data>('./msg.json').then(res=>{

console.log(res,res.code,res.msg);

});

这样请求不同的数据时只要修改Data的类型而不需要修改get函数 ,增强了函数的通用性

泛型约束

泛型约束:减少泛型的范围

泛型可以指定能接受的类型,使得泛型只能固定成指定的类型

<code>interface Len{

length:number

}

// T只接受有length属性的类型

function getLen<T extends Len>(parmas:T):number{

return parmas.length;

}

getLen('abc');//string类型有length属性

getLen([1,2,3]);//数组类型有length属性

getLen({length:10});//有length属性的对象

以上是获取参数的length属性,使用extends约束了T,T类型是有length属性的类型,表示参数parmas的类型中需要有length属性,

keyof

keyof:获取对象的key

泛型约束常常会使用属性,来约束某种类型需要这个属性,使用keyof可以获取到对象的属性

// keyof:获取对象的key

let obj = {

name:'a',

age:10

}

// 获取obj类型的key的类型

type O = keyof typeof obj;//O的类型为'name'|'age'

function getValue<T extends object,K extends keyof T>(obj:T,key:K){

// T限制为对象,K限制为T的key

return obj[key];

}

getValue(obj,'name');//name是obj的key

getValue(obj,'age');//age是obj的key

以上的getValue函数中T被约束成只能是对象类型,K被约束成是对象的 属性

keyof的扩展用法

// keyof的扩展用法

interface Obj{

name:string,

age:number

}

// 使用keyof 获取接口类型的属性,并重新对属性定义类型规则,在赋给新属性

// 这样就实现了基于已有类型,创建新的类型,(相当于类型函数,对类型进行操作)

type Options<T extends object> = {

[K in keyof T]?:T[K]

}

//这里相当于变成了可选属性的Obj类型,Obj替代了T

type L = Options<Obj>;//L的类型为{ name?:string; age?:number; }

以上使用了keyof,遍历了约束成对象类型的泛型T的属性,并将属性接受值的类型修改成了可选?:,再赋值给Options类型,

这里实现了一个类似函数的效果,可以把Options看成是一个‘类型函数’,将传入的类型属性全部修改成可选,

总结

泛型的存在让ts的类型变得非常灵活,使类型变得可以修改,可以变化不在固定死板;学习了泛型之后,对于ts类型的理解应该更加深入,ts类型其实就相当于一个特殊的值,这个值使专门用来约束变量的值,值可以被类型约束,类型也是一个值,类型也可以被类型约束,类型是一个隐式的值,它不会影响代码的结果,它会约束代码的执行,再执行前将代码不合理的类型错误排除出来;同时在编辑代码时,提示这种类型变量的具有的属性,让整个代码更加具有逻辑性

 完整代码展示

// 泛型,动态类型,

// 定义函数时,对于未知的返回值和参数类型,可以使用泛型,在使用函数时,会将动态类型固定,

// 定义类型时, 对于未知的类型,可以使用泛型,在给泛型的变量赋值时,会将动态类型固定

// 同名的泛型表示是同一种类型,在固定类型后,同名泛型也会被相同的类型固定

// 将参数合成数组

function add<T>(a:T,b:T):Array<T>{

return [a,b];

}

add(1,2) //这里的T被固定成number

add('a','b') //这里的T被固定成string

add(true,false)// 这里的T被固定成boolean

// 泛型的特点

// 1. 泛型可以指定多个,如<T,U>

// 2. 泛型可以指定默认类型,如<T=number>

// 3. 泛型可以指定联合类型

type Ab<T,U=object> = number | string | T | U;

let ab:Ab<boolean>;

ab =20;

ab ='a';

ab =true;

ab = {};

// 封装模拟axios的get方法, 返回一个Promise,Promise成功时返回请求结果

const axios = {

get<T>(url:string):Promise<T>{

return new Promise((resolve,reject)=>{

let xhr:XMLHttpRequest = new XMLHttpRequest();

xhr.open('GET',url);

xhr.onload = ()=>{

if(xhr.status >= 200 && xhr.status < 300){

resolve(JSON.parse(xhr.response));

}

}

xhr.send();

})

}

}

interface Data{//指定返回数据的类型,否则对于不明确的类型,无法固定泛型,默认会定为unknow

code:number,

msg:string

}

axios.get<Data>('./msg.json').then(res=>{

console.log(res,res.code,res.msg);

});

// 泛型约束:减少泛型的范围

// 泛型可以指定能接受的类型,使得泛型只能固定成指定的类型

interface Len{

length:number

}

// T只接受有length属性的类型

function getLen<T extends Len>(parmas:T):number{

return parmas.length;

}

getLen('abc');//string类型有length属性

getLen([1,2,3]);//数组类型有length属性

getLen({length:10});//有length属性的对象

// keyof:获取对象的key

let obj = {

name:'a',

age:10

}

// 获取obj类型的key的类型

type O = keyof typeof obj;//O的类型为'name'|'age'

function getValue<T extends object,K extends keyof T>(obj:T,key:K){

// T限制为对象,K限制为T的key

return obj[key];

}

getValue(obj,'name');//name是obj的key

getValue(obj,'age');//age是obj的key

// keyof的扩展用法

interface Obj{

name:string,

age:number

}

// 使用keyof 获取接口类型的属性,并重新对属性定义类型规则,在赋给新属性

// 这样就实现了基于已有类型,创建新的类型,(相当于类型函数,对类型进行操作)

type Options<T extends object> = {

[K in keyof T]?:T[K]

}

//这里相当于变成了可选属性的Obj类型,Obj替代了T

type L = Options<Obj>;//L的类型为{ name?:string; age?:number; }



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。