写在开头。写了一年多的TypeScript,总结了以下几点。希望对大家有所帮助。向对象接口添加属性时遇到的问题Obj{a:string;}constobj:Obj={a:"1",};obj.b=2;这时候会出现错误信息:类型“Obj”不存在属性“b”。为了解决这个问题,使用索引签名接口Obj{a:string;[索引:字符串]:字符串|数字;}constobj:Obj={a:"1",};obj.b=2;好奇,为什么我要加上[index:string]:string|number;,类型为字符串或数字。因为:声明一个索引签名时,所有显式成员必须符合索引签名函数重载场景:函数有多个参数,当参数不确定时,函数运行逻辑不一致//重载函数padding(all:number);functionpadding(topAndBottom:number,leftAndRight:number);functionpadding(top:number,right:number,bottom:number,left:number);//实际实现是函数体所有情况的真实表示需要处理函数填充(a:number,b?:number,c?:number,d?:number){if(b===undefined&&c===undefined&&d===undefined){b=c=d=一个;}elseif(c===undefined&&d===undefined){c=a;d=b;}return{top:a,right:b,bottom:c,left:d};}该函数兼容传递1、2、4参数。但是只要传了三个就会报错。最重要的是最终的声明(从函数内部看到的真实声明)兼容所有重载(与上面的索引签名一致)下载的第三方npm库没有ts声明文件例如:npmisomePackage--saveimportsomePackagefrom'somePackage';但此时提示:找不到模块“somePackage”或其对应的类型声明。此时,可以在项目根目录下新建一个index.d.ts,写入如下代码:declaremodule'somePackage';...这样问题就解决了。泛型很容易给小白带来麻烦。其实泛型就是简单的A类型变量,如下所示:classPeter{niubi(a:T):T[]{return[a];}}这时候T就是泛型,也就是变量类型。是根据你传入niubi方法的参数对象来决定的。当我们传入的a是一个字符串,那么T就是一个字符串。返回的是项目字符串数组。Peter类{niubi(a:T):T[]{return[a];}}constobj=newPeter();letres=obj.niubi("hehe");res=1;res=["2"];res=1会报错类型"number"无法赋值改为“string[]”类型,因为此时TS推断res一定是一个数组,里面的item是一个字符串。res=["2"]不会报错泛型可以说是TS中的一个难点,但实际上它只是一个变量类型的变量。调整参数后:设res2=obj.niubi(2);res2=2;会报错:类型“number”不能赋值给类型“number[]”。最后要记住的是,因为它是一个类型变量。那么这个变量也可以是一个泛型。Peter类{niubi(a:T):T[]{return[a];}}constobj=newPeter();functiontest(b:T):T{returnb;}letres=obj.niubi(test(1));看到这里,肯定有人会说,Peter,脱裤子放屁。这比使用任何一个都好。那你看下面的代码,我们在封装api请求的时候。首先定义返回的接口。(返回的接口一般都有统一的格式、状态码、结果、数据等)//请求接口数据导出接口ResponseData{/***状态码*@type{number}*/code:数字;/***数据*@type{T}*/结果:T;/***message*@type{string}*/message:string;}这里的data数据是动态格式的,我们可以使用pantype来定义。这里使用了两次泛型,首先定义返回数据data,然后以泛型的方式传入,组装整个返回数据接口(包括代码、结果、数据)。然后传入真正的请求函数//axios在axios.ts文件中进行处理,比如添加通用配置,拦截器等importAxfrom'./axios';import{ResponseData}from'./interface.ts';exportfunctiongetUser(){returnAx.get>('/somepath').then(res=>res.data).catch(err=>console.error(err));}真正的请求函数中使用了泛型,即传入任意类型参数,然后返回一个Promise风格的Promise数据:constget=(config:{url:string;headers?:{[key:string]:string}}):Promise=>{constfetchConfig={方法:'GET',Accept:'application/json','Content-Type':'application/json',...(config.headers||{})};returnfetch(config.url,fetchConfig).then(response=>response.json());};总结两次泛型连续使用:1.将data作为泛型类型传入2.组装成{code,result,data}类型接口3.将第二步组装好的类型作为泛型传入get方法4.返回一个Promise风格的Promise数据。这样做的意思就是把可变数据类型的数据抽取出来,让TS推断这个接口返回的数据是什么样的。减少不必要的重复代码,即每次接口调用都会返回的数据格式类型:code和result相信通过这段代码和你能真正明白TS泛型的使用方法,用在什么地方,使用的意义上文字。细粒度定义类型后的问题当我们细粒度定义接口时,可能会出现接口复用的问题,例如:interfacetest1{a:string;}interfacetest2{b:string;}这时候,我想定义一个同时具备这两个属性的对象,那么可以使用联合类型。constobj:test1&test2={a:"1",b:"2",};如果我想定义一个只有a/b的对象,我可以使用constobj:test1|test2={a:"1",};可能有人会说,这么简单的东西怎么写。这是为接下来的类型兼容打下基础。TS中最重要的就是类型类型,类型系统是它的核心。我们可以使用两个不同的变量互相赋值来检查它们的类型是否兼容,例如:interfaceTest1{a:number;b:数量;c:string;}interfaceTest2{a:number;b:数字;}lettest1:Test1={a:1,b:2,c:"3",};lettest2:Test2={a:1,b:2,};test1=test2;此时,提示类型为“Test2”缺少属性“c”,但在类型“Test1”中是必需的。但是当我们使用test1给test2赋值时:test2=test1;这时候是可以的。其实这里隐藏了一些逻辑。Test1接口比Test2接口多了一个c属性。Test2接口可以说是Test1接口的子类。多态就是这样处理和判断TS的类型兼容性的。可以看到以下类型的协变(Covariant):仅在同一个方向;逆变(Contravariant):只在相反的方向;双向协变(Bivariant):包括同向和异向;不变性:如果类型不完全相同,则它们是不兼容的。写在最后