背景用过Typescript的人都知道,很多时候我们需要提前声明一个类型,然后将类型赋值给一个变量。比如在业务中,我们需要渲染一个表格,往往需要定义:interfaceRow{user:stringemail:stringid:numbervip:boolean//...}consttableDatas:Row[]=[]//...有时候我们也需要表格对应的搜索表单,需要一两个搜索项。如果你是typescript新手,可能立马这样写:interfaceSearchModel{user?:stringid?:number}constmodel:SearchModel={user:'',id:undefined}这样写的时候会有问题方式。如果后面要把id类型改成string,我们需要改两处。如果我们不小心,我们可能会忘记更改其他地方。所以,有人会这样写:interfaceSearchModel{user?:Row['user']id?:Row['id']}这当然是一种解决方案,但其实我们前面已经定义了Row类型,means其实可以更优雅地复用:constmodel:Partial={user:'',id:undefined}//或者需要显式指定key,可以constmodel2:Partial>这样,在很多情况下,我们可以尽可能少地编写重复的类型,复用已有的类型,让代码更优雅,更易于维护。上面使用的Partial和Pick都是typescript内置的类型别名。下面给大家介绍下typescript常用的内置类型,以及可以自己扩展的类型。打字稿内置类型Partial将类型T的所有属性标记为可选属性typePartial={[PinkeyofT]?:T[P];};使用场景://账户属性接口AccountInfo{name:stringemail:stringage:numbervip:0|1//1为vip,0为非vip}//当我们需要渲染一个账户表时,我们需要defineconstaccountList:AccountInfo[]=[]//但是当我们需要查询和过滤账户信息的时候,我们需要传递表单,//但显然我们可能不一定需要使用所有的属性来搜索,那么我们可以定义constmodel:Partial={name:'',vip:undefind}Required与Partial相反,Required将类型T的所有属性标记为必需属性typeRequired={[PinkeyofT]-?:T[P];};Readonly设置所有属性标记为只读,即不可修改typeReadonly={readonly[PinkeyofT]:T[P];};Pick过滤掉属性KtypePick={[PinK]:T[P];};使用场景:interfaceAccountInfo{name:stringemail:stringage:numbervip?:0|1//1为vip,0为非vip}typeCoreInfo=Pick/*{name:stringemail:stirng}*/RecordkeyvaluetypetypeRecord<Kextendskeyofany,T>={[PinK]:T;};使用场景://定义学号(key)-账户信息(value)对象constaccountMap:Record={10001:{name:'xx',email:'xxxxx',//...}}constuser:Record<'name'|'email',string>={name:'',email:''}//复杂点函数的类型推断mapObject(obj:Record,f:(x:T)=>U):Recordconstnames={foo:"hello",bar:"world",baz:"bye"};//这里推断K,T是字符串,U是数字constlengths=mapObject(names,s=>s.length);//{foo:number,bar:number,baz:number}Exclude,Omit从T类型中移除U类型Exclude=TextendsU?never:T;使用场景://'a'|'d'typeA=Exclude<'a'|'b'|'c'|'d','b'|'c'|'e'>乍一看好像没什么用,但是经过一些操作,我们可以得到Pick的逆操作:typeOmit=Pick>typeNonCoreInfo=Omit/*{age:numbervip:0|1,}*/ExtractExclude的逆运算,取T和U类型的交集属性Extract=TextendsU?T:从不;使用demo://'b'|'c'typeA=Extract<'a'|'b'|'c'|'d','b'|'c'|'e'>这看起来没用,在事实上,它真的没用。应该是我孤陋寡闻,还没发现它的用处。NonNullable排除空值|T类型的未定义属性NonNullable=Textendsnull|不明确的?从不:T;使用演示类型A=字符串|编号|未定义类型B=NonNullable//字符串|numberfunctionf2(x:T,y:NonNullable){让s1:string=x;//错误,x可能未定义lets2:string=y;//Ok}Parameters获取一个函数的所有参数类型//这里使用inferP将参数设置为要推断的类型//当T符合函数特性时,返回参数类型,否则返回nevertype参数any>=Textends(...args:inferP)=>any?P:从不;usedemo:interfaceIFunc{(person:IPerson,count:number):boolean}typeP=Parameters//[IPerson,number]constperson01:P[0]={//...}另一种使用场景就是快速获取未知函数的参数类型import{somefun}from'somelib'//从其他库导入的函数,获取其参数类型SomeFuncParams=Parameters//内置函数//[any,number?,number?]typeFillParams=ParametersConstructorParmeters类似于Parameters,ConstructorParameters获取类类型的构造函数参数ConstructorParametersany>=Textendsnew(...args:inferP)=>任何?P:从不;使用demo://字符串|编号|DatetypeDateConstrParams=ConstructorParametersReturnType获取函数类型T的返回类型ReturnTypeany>=Textends(...args:any)=>推断R?R:任何;用法与Parameters类似,不再赘述InstanceType获取类的返回类型类型InstanceTypeany>=Textendsnew(...args:any)=>推断R?R:任何;用法与ConstructorParameters类似,不再详述自定义TypeWeaken使用typescript时,有时需要重写一个库提供的接口的属性,但重写接口可能会引起冲突:interfaceTest{name:stringsay(word:string):string}interfaceTest2extendsTest{名称:测试['名称']|number}//错误:输入'string|number'不可分配给类型'string'。然后你可以使用一些类型来拯救国家现在我们的需求://原则是将类型T的K个属性全部设置为any,//然后自定义K个属性的类型,//因为任意类型都可以赋值给任意,所以不会有冲突typeWeaken={[PinkeyofT]:PextendsK?any:T[P];};interfaceTest2extendsWeaken{名称:Test['name']|}//okarray有时会转换为unionconstALL_SUITS=['hearts','diamonds','spades','clubs']asconst;//TS3.4typeSuitTuple=typeofALL_SUITS;//readonly['hearts','diamonds','spades','clubs']typeSuit=SuitTuple[number];//联合类型:'hearts'|'钻石'|'黑桃'|'clubs'根据枚举生成unionenumkeyvalueunionenumWeekday{Mon=1Tue=2Wed=3}typeWeekdayName=keyoftypeofWeekday//'Mon'|'星期二'|'Wed'枚举不能实现值联合,但可以对象的值联合constlit=(v:V)=>v;constWeekday={MONDAY:lit(1),TUESDAY:lit(2),WEDNESDAY:lit(3)}typeWeekday=(typeofWeekday)[keyoftypeofWeekday]//1|2|3PartialRecord前面我们讲了Record类型,我们会经常用到接口Model{name:stringemail:stringid:numberage:number}//定义表单的验证规则constvalidateRules:Record={name:{required:true,trigger:`blur`},id:{required:true,trigger:`blur`},email:{required:true,message:`...`},//error:Propertyageismissingintype...}这里有问题,validateRules键值必须匹配所有的Model,两者缺一不可,但实际上我们的表单可能只有其中一两个。这时候我们需要:typePartialRecord=Partial>constvalidateRules:PartialRecord={name:{required:true,trigger:`blur`}}这个例子结合了打字稿中的内置类型别名Partial和PartialUnpacked解压缩并提取密钥类型typeUnpacked=Textends(inferU)[]?U:Textends(...args:any[])=>inferU?U:TextendsPromise?U:T;typeT0=Unpacked;//字符串类型T1=Unpacked;//stringtypeT2=Unpacked<()=>string>;//字符串类型T3=Unpacked>;//字符串类型T4=Unpacked[]>;//PromisetypeT5=Unpacked[]>>;//stringsummarizesfact,基于已有的类型Aliases,以及新推出的infer类型进行推断,可以探索出多种复杂的组合玩法,这里就不多说了,大家慢慢探索吧。谢谢阅读!