当前位置:首页 > 数码 > 从基础到实战运行-一文读懂-TypeScript-泛型 (从基础到实战:全面讲解补液)

从基础到实战运行-一文读懂-TypeScript-泛型 (从基础到实战:全面讲解补液)

admin7个月前 (04-24)数码33

泛型是静态类型言语的基本特征,准许将类型作为参数传递给另一个类型、函数、或许其他结构。TypeScript支持泛型作为将类型安保引入组件的一种形式。这些组件接受参数和前往值,其类型将是不确定的,直到它在代码中被经常使用。上方将经过一些示例,探求如何在函数、类型、类和接口中经常使用泛型,以及经常使用泛型创立映射类型和条件类型。

1.泛型语法

首先来看看TypeScript泛型的语法。泛型的语法为<T>,其中T示意传入的类型。在这种状况下,T和函数参数的上班形式相反,其作为将在创立结构实例时申明的类型的占位符。因此,尖括号内指定的泛型类型也称为泛型类型参数。泛型的定义可以有多个泛型类型采参数,例如:<T,K,Z>。

上方经过一个函数的例子来看看泛型的基本语法。假定有一个Script函数,它接受两个参数:一个对象和一个蕴含key的数组。该函数将基于原始对象前往一个新对象,其仅蕴含想要的key:

functionpickObjectKeys(obj,keys){letresult={}for(constkeyofkeys){if(keyinobj){result[key]=obj[key]}}returnresult}

在pickObjectKeys()函数中,遍历了keys数组并经常使用数组中指定的key创立一个新对象。上方来测试一下这个函数:

constlanguage={name:"TypeScript",age:8,extensions:['ts','tsx']}constageAndExtensions=pickObjectKeys(language,['age','extensions'])

这里申明了一个language对象,而后经常使用pickObjectKeys()函数将language对象中的age和extensions属性组成了一个新的对象ageAndExtensions,其值如下:

{age:8,extensions:['ts','tsx']}

假构想将这个函数迁徙到TypeScript以使其类型安保,则可以经常使用泛型。重构的代码如下:

functionpickObjectKeys<T,KextendskeyofT>(obj:T,keys:K[]){letresult={}asPick<T,K>for(constkeyofkeys){if(keyinobj){result[key]=obj[key]}}returnresult}constlanguage={name:"TypeScript",age:8,extensions:['ts','tsx']}constageAndExtensions=pickObjectKeys(language,['age','extensions'])

<T,KextendskeyofT>为函数申明了两个参数类型,其中K被调配给了一个类型,该类型是T中的key的汇合。而后将obj参数设置为T,示意任何类型,并将keys设置为数组,无论K是什么类型。

当传入的obj参数为language对象时,T将age设置为number类型,将extensions设置为string[]类型,所以变量ageAndExtensions的类型为:

{age:number;extensions:string[];}

这样就会依据提供应pickObjectKeys的参数来判别前往值的类型,从而准许函数在知道须要强迫口头的特定类型之前灵敏地强迫口头类型结构。当在VisualStudioCode等IDE中经常使用该函数时,这使得开发体验更好,它将依据提供的对象为keys参数提供倡导:

图片

2.在函数中经常使用泛型

将泛型与函数一同经常使用的最经常出现场景之一就是,当有一些不容易为一切的用例定义类型时,为了使该函数适用于更多状况,就可以经常使用泛型来定义。上方来看看在函数中经常使用泛型的经常出现场景。

(1)调配泛型参数

先来看上方的函数,它前往函数参数传入的内容:

functionidentity(value){returnvalue;}

可以为其参与泛型类型以使函数的类型更安保:

functionidentity<T>(value:T):T{returnvalue;}

这里将函数转化为接受泛型类型参数T的泛型函数,它第一个参数的类型,而后将前往类型也设置为T。上方来测试一下这个函数:

functionidentity<T>(value:T):T{returnvalue;}constresult=identity(123);

result的类型为123,这是咱们传入的数字:

图片

此时,TypeScript经常使用调用代码自身来推断泛型类型。这样调用代码不须要传递任何类型参数。当然,咱们也可以显式地将泛型类型参数设置为想要的类型:

functionidentity<T>(value:T):T{returnvalue;}constresult=identity<number>(123);

在这段代码中,result的类型就是number:

图片

这里经常使用<number>定义了传入类型,让TypeScript标识函数的泛型类型参数T为number类型。这将强迫number类型作为参数和前往值的类型。当再传入其他类型时,就会报错:

图片

(2)间接传递类型参数

在经常使用自定义类型时,间接传递类型参数也很有用。来看上方的代码:

typeProgrammingLanguage={name:string;};functionidentity<T>(value:T):T{returnvalue;}constresult=identity<ProgrammingLanguage>({name:"TypeScript"});

在这段代码中,result为自定义类型ProgrammingLanguage,它间接传递给了identity函数。假设没有显式地定义类型参数,则result的类型就是{name:string}。

另一个经常出现的例子就是经常使用函数从API失掉数据:

asyncfunctionfetchApi(path:string){constresponse=awtfetch(`${path}`)returnresponse.json();}

这个异步函数将URL门路path作为参数,经常使用fetchAPI向URL收回恳求,而后前往JSON照应值。在这种状况下,fetchApi函数的前往类型是Promise<any>,这是fetch的照应答象的json()调用的前往类型。

将any作为前往类型并不会有任何作用,它示意恣意类型,经常使用它将失去静态类型审核。假设咱们知道API将前往指定结构的对象,则可以经常使用泛型以使此函数类型更安保:

asyncfunctionfetchApi<ResultType>(path:string):Promise<ResultType>{constresponse=awaitfetch(`${path}`);returnresponse.json();}

这里就将函数转换为接受ResultType泛型类型参数的泛型函数。此泛型类型用于函数的前往类型:Promise<ResultType>。

可以看到,泛型并没有在参数列表中经常使用,也没有在TypeScript能够推断其值的其他中央经常使用。这象征着在调用函数时,必定显式地传递此泛型的类型:

全面讲解补液
typeUser={name:string;}asyncfunctionfetchApi<ResultType>(path:string):Promise<ResultType>{constresponse=awaitfetch(`${path}`);returnresponse.json();}const>asyncfunctionfetchApi<ResultType>(path:string):Promise<ResultType>{constresponse=awaitfetch(`${path}`);returnresponse.json();}const>asyncfunctionfetchApi<ResultType=Record<string,any>>(path:string):Promise<ResultType>{constresponse=awaitfetch(`${path}`);returnresponse.json();}const>functionstringifyObjectKeyValues<TextendsRecord<string,any>>(obj:T){returnObject.keys(obj).reduce((acc,key)=>({...acc,[key]:JSON.stringify(obj[key])}),{}as{[KinkeyofT]:string})}

在这段代码中,stringifyObjectKeyValues函数经常使用reduce数组方法遍历蕴含原始对象的key的数组,将属性值字符串化并将它们参与到新数组中。

为确保调用代码一直传入一个对象作为参数,可以在泛型类型T上经常使用类型解放:

functionstringifyObjectKeyValues<TextendsRecord<string,any>>(obj:T){//...}

extendsRecord<string,any>被称为泛型类型解放,它准许指定泛型类型必定可调配给extends关键字之后的类型。在这种状况下,Record<string,any>示意具备string类型的键和any类型的值的对象。咱们可以使类型参数裁减当何有效的TypeScript类型。

在调用reduce时,reducer函数的前往类型是基于累加器的初始值。{}as{[KinkeyofT]:string}经过对空对象{}经常使用类型断言将累加器的初始值的类型设置为{[KinkeyofT]:string}。type{[KinkeyofT]:string}创立了一个新类型,其键与T相反,但一切值都设置为字符串类型,这称为映射类型。

上方来测试一下这个函数:

functionstringifyObjectKeyValues<TextendsRecord<string,any>>(obj:T){returnObject.keys(obj).reduce((acc,key)=>({...acc,[key]:JSON.stringify(obj[key])}),{}as{[KinkeyofT]:string})}conststringifiedValues=stringifyObjectKeyValues({a:"1",b:2,c:true,d:[1,2,3]})

变量stringifiedValues的类型如下:

{a:string;b:string;c:string;d:string;}

3.在接口、类和类型中经常使用泛型

在TypeScript中创立接口和类时,经常使用泛型类型参数来设置结果对象的类型会很有用。例如,一个类或许具备不同类型的属性,详细取决于传入结构函数的内容。上方就来看看在类和接口中申明泛型类型参数的语法。

(1)接口和类中的泛型

要创立泛型接口,可以在接口称号后参与类型参数列表:

interfaceMyInterface<T>{field:T}

这里申明了一个具备field字段的接口,field字段的类型由传入T的类型确定。

关于类,它的语法和接口定义简直是相反的:

classMyClass<T>{field:Tconstructor(field:T){this.field=field}}

通用接口/类的一个经常出现例子就是当有一个类型取决于如何经常使用接口/类的字段。假定有一个Httplication类,用于处置对API的HTTP恳求,并且某些context值将被传递给每个恳求处置程序。代码如下:

classHttpApplication<Context>{context:Contextconstructor(context:Context){this.context=context;}//...get(url:string,handler:(context:Context)=>Promise<void>):this{//...returnthis;}}

这个类贮存了一个context,它的类型作为get方法中handler函数的参数类型传入。在经常使用时,传递给get方法的handler的参数类型将从传递给类结构函数的内容中推断进去:

constcontext={someValue:true};constapp=newHttpApplication(context);app.get('/api',async()=>{console.log(context.someValue)});

在这段代码中,TypeScript会将context.someValue的类型推断为boolean。

(2)自定义类型中的泛型

将泛型运行于类型的语法相似于它们运行于接口和类的形式。来看上方的代码:

typeMyIdentityType<T>=T

这个泛型类型前往类型参数传递的类型。经常使用以下代码来成功这种类型:

typeB=MyIdentityType<number>

在这种状况下,B的类型就是number。

泛型类型通罕用于创立工具类型,尤其是在经常使用映射类型时。TypeScript内置了许多工具类型。例如Partial适用工具类型,它传入类型T并前往另一种具备与T相反的类型,但它们的一切字段都设置为可选。Partial的成功如下:

typePartial<T>={[PinkeyofT]?:T[P];};

这里,Partial接受一个类型,遍历它的属性类型,而后将它们作为可选的新类型前往。

4.经常使用泛型创立映射类型

经常使用TypeScript时,有时须要创立一个与另一种类型具备相反结构的类型。这象征着它们应该具备相反的属性,但属性的类型不同。关于这种状况,经常使用映射类型可以重用初始类型并缩小重复代码。这种结构称为映射类型并依赖于泛型。上方就来看看如何创立映射类型。

先来看一个例子,给定一种类型,前往一个新类型,其中一切属性值都设置为boolean类型。可以经常使用以下代码创立此类型:

typeBooleanFields<T>={[KinkeyofT]:boolean;}

在这种类型中,经常使用[KinkeyofT]指定新类型将具备的属性。keyofT用于前往T中一切可用属性的称号。而后经常使用Kin来指定新类型的属性是keyofT前往的类型中可用的一切属性。

这将创立一个名为K的新类型,该类型就是以后属性的称号。可以用于经常使用T[K]语法来访问原始类型中此属性的类型。在这种状况下,将属性值的类型设置为boolean。

这种BooleanFields类型的一个经常使用场景是创立一个选项对象。假定有一个数据库模型,例如User。从数据库中失掉此模型的记载时,还可以传递一个指定要前往哪些字段的对象。该对象将具备与模型相反的属性,但类型设置为布尔值。在字段中传递true象征着宿愿它被前往,而false则象征着宿愿它被省略。

可以在现有模型类型上经常使用BooleanFields泛型来前往与模型具备相反结构的新类型,但一切字段都设置为布尔类型,代码如下所示:

typeBooleanFields<T>={[KinkeyofT]:boolean;};typeUser={email:string;name:string;}typeUserFetchOptions=BooleanFields<User>;

UserFetchOptions的类型如下:

typeUserFetchOptions={email:boolean;name:boolean;}

在创立映射类型时,还可以为字段提供润色符,例如Readonly<T>。Readonly<T>类型前往一个新类型,其中传递类型的一切属性都设置为只读属性。这种类型的成功如下:

typeReadonly<T>={readonly[KinkeyofT]:T[K]}

目前,可以在映射类型中经常使用的两个可用润色符是readonly润色符,它必定作为前缀参与到属性中,用于将属性设置为只读;以及?润色符,可以作为后缀参与到属性中,用于将属性设置为可选。

5.经常使用泛型创立条件类型

上方来看看如何经常使用泛型创立条件类型。

(1)基础条件类型

条件类型是泛型类型,依据某些条件具备不同的结果类型。先来看看上方的泛型类型IsStringType<T>:

typeIsStringType<T>=Textendsstring?true:false;

在这段代码中,创立了一个名为IsStringType的新泛型类型,它接纳一个类型参数T。在类型定义中,经常使用的语法相似于JavaScript中的三元表白式。此条件表白式审核类型T能否是string类型。假设是,结果的类型将是true;否则,结果的类型将是false。

要尝试这种条件类型,须要将类型作为其类型参数传递:

typeIsStringType<T>=Textendsstring?true:false;typeA="abc";typeB={name:string;};typeResultA=IsStringType<A>;typeResultB=IsStringType<B>;

在此代码中,创立了两种类型:A和B。A是字符串字面量类型abc,B是具备string类型的name属性的对象的类型。将这两种类型与IsStringType条件类型一同经常使用,并将结果类型存储到两个新类型ResultA和ResultB中。

这里ResultA类型设置为true,而ResultB类型设置为false。由于A确实裁减了字符串类型,而B没有裁减字符串类型,由于它被设置为具备字符串类型的单个name属性的对象的类型。

条件类型的一个有用个性是它准许经常使用不凡关键字infer在extends中推断类型信息。可以在条件为真的分支中经常使用这种新类型。此配置的一种用途是检索任何函数类型的前往类型。

例如,GetReturnType类型定义如下:

typeGetReturnType<T>=Textends(...args:any[])=>inferU?U:never;

在这段代码中,创立了一个新的泛型类型,它是一个名为GetReturnType的条件类型。此泛型类型接受一个类型参数T。在类型申明自身外部,审核类型T能否裁减了与接受可变数量参数(包括0)的函数签名婚配的类型,而后推断该前往函数的类型,创立一个新类型U,它可用于条件的实在分支。U的类型将绑定到传递函数的前往值的类型。假设传递的类型T不是函数,则代码将前往类型nerver。

将此类型与以下代码一同经常使用:

typeGetReturnType<T>=Textends(...args:any[])=>inferU?U:never;functionsomeFunction(){returntrue;}typeReturnTypeOfSomeFunction=GetReturnType<typeofsomeFunction>;

在这段代码中,创立了一个名为someFunction的函数,该函数前往true。而后,经常使用typeof运算符将此函数的类型传递给GetReturnType泛型,并将结果类型保管在ReturnTypeOfSomeFunction中。

由于someFunction变量的类型是函数,因此条件类型将计算条件为真的分支。这将前往类型U作为结果。U类型是依据函数的前往类型推断进去的,在本例中是boolean。假设审核ReturnTypeOfSomeFunction的类型,会发现它被设置为了boolean类型。

(2)初级条件类型

条件类型是TypeScript中最灵敏的配置之一,准许创立一些初级适用程序类型。接上去就创立一个名为NestedOmit<T,KeysToOmit>的类型,它可以省略对象中的字段,就像现有的Omit<T,KeysToOmit>适用程序类型一样,但也准许经常使用点示意法省略嵌套字段。

经常使用新的NestedOmit<T,KeysToOmit>泛型,将能够经常使用上方例子中的类型:

typeSomeType={a:{b:string,c:{d:number;e:string[]},f:number}g:number|string,h:{i:string,j:number,},k:{l:number,<F3>}}typeResult=NestedOmit<SomeType,"a.b"|"a.c.e"|"h.i"|"k">;

这段代码申明了一个名为SomeType的类型,该类型具备嵌套属性的多级结构。经常使用NestedOmit泛型传入类型,而后列出想要省略的属性的key。第二个类型参数中经常使用点符号来标识要省略的键。而后将结果类型存储在Result中。

结构此条件类型将经常使用TypeScript中的许很多配置,例如模板文本类型、泛型、条件类型和映射类型。

首先创立一个名为NestedOmit的泛型类型,它接受两个类型参数:

typeNestedOmit<TextendsRecord<string,any>,KeysToOmitextendsstring>

第一个类型参数为T,它必定是可调配给Record<string,any>类型的类型,它是要从中省略属性的对象的类型。第二个类型参数为KeysToOmit,它必定是string类型。经常使用它来指定要从类型T中省略的key。

接上去须要判别KeysToOmit能否可调配给类型${inferKeyPart1}.${inferKeyPart2}:

typeNestedOmit<TextendsRecord<string,any>,KeysToOmitextendsstring>=KeysToOmitextends`${inferKeyPart1}.${inferKeyPart2}`

这里就用到了模板文本类型,同时应用条件类型在模板文字中推断出其他两种类型。经过这两局部,将一个字符串拆分为了两个字符串。第一局部将调配给类型KeyPart1并将蕴含第一个点之前的一切内容。第二局部将调配给类型KeyPart2并将蕴含第一个点之后的一切内容。假设将a.b.c作为KeysToOmit传递,则最后KeyPart1将设置为字符串类型a,而KeyPart2将设置为b.c。

接上去,经常使用三元表白式来定义条件为true的分支:

typeNestedOmit<TextendsRecord<string,any>,KeysToOmitextendsstring>=KeysToOmitextends`${inferKeyPart1}.${inferKeyPart2}`?KeyPart1extendskeyofT

这里经常使用KeyPart1extendskeyofT来审核KeyPart1能否是给定类型T的有效属性。假设是一个有效的key,经常使用以下代码以使条件计算为两种类型之间的交加:

typeNestedOmit<TextendsRecord<string,any>,KeysToOmitextendsstring>=KeysToOmitextends`${inferKeyPart1}.${inferKeyPart2}`?KeyPart1extendskeyofT?Omit<T,KeyPart1>&{[NewKeysinKeyPart1]:NestedOmit<T[NewKeys],KeyPart2>}

Omit<T,KeyPart1>是经常使用TypeScript自带的Omit构建的类型。此时,KeyPart1不是点示意法:它将蕴含一个字段确实切称号,该字段蕴含要从原始类型中省略的嵌套字段。因此,可以安保地经常使用现有的适用程序类型。

经常使用Omit删除T[KeyPart1]内的一些嵌套字段,为此,必定重重生成T[KeyPart1]的类型。为防止重重生成整个T类型,经常使用Omit从T中仅删除KeyPart1,保管其他字段。而后,将在下一局部的类型中重建T[KeyPart1]。

[NewKeysinKeyPart1]:NestedOmit<T[NewKeys],KeyPart2>是一个映射类型,其中属性是可调配给KeyPart1的属性,也就是上方从KeysToOmit中提取的局部。这是须要删除的字段的父级。假设传入了a.b.c,那么在第一次性它将是a中的NewKeys。而后,将此属性的类型设置为递归调用NestedOmit适用程序类型的结果,但如今经常使用T[NewKeys]作为第一个类型参数传递T内的此属性的类型,并作为第二个类型参数传递点符号的其他key,在KeyPart2中可用。

在外部条件为false分支中,前往绑定到T的以后类型,就如同KeyPart1不是T的有效key:

typeNestedOmit<TextendsRecord<string,any>,KeysToOmitextendsstring>=KeysToOmitextends`${inferKeyPart1}.${inferKeyPart2}`?KeyPart1extendskeyofT?Omit<T,KeyPart1>&{[NewKeysinKeyPart1]:NestedOmit<T[NewKeys],KeyPart2>}:T

条件的这个分支象征着省略T中不存在的字段。在这种状况下,无需再进一步。最后,在外部条件为false的分支中,经常使用内置的Omit适用程序类型从T中省略KeysToOmit:

typeNestedOmit<TextendsRecord<string,any>,KeysToOmitextendsstring>=KeysToOmitextends`${inferKeyPart1}.${inferKeyPart2}`?KeyPart1extendskeyofT?Omit<T,KeyPart1>&{[NewKeysinKeyPart1]:NestedOmit<T[NewKeys],KeyPart2>}:T:Omit<T,KeysToOmit>;

假设条件KeysToOmitextends${inferKeyPart1}.${inferKeyPart2}为false,则示意KeysToOmit未经常使用点示意法,因此可以经常使用Omit适用程序类型。

如今,要经常使用新的NestedOmit条件类型,创立一个名为NestedObject的类型:

typeNestedObject={a:{b:{c:number;d:number;};e:number;};f:number;};

调用NestedOmit以省略a.b.c中可用的嵌套字段:

typeResult=NestedOmit<NestedObject,"a.b.c">;

在条件类型的第一次性计算中,外部条件为真,由于字符串字面量类型a.b.c可调配给模板文本类型${inferKeyPart1}.${inferKeyPart2}。在这种状况下,KeyPart1将被推断为字符串字面量类型a,而KeyPart2将被推断为字符串的残余局部,在本例中为b.c。

上方将计算外部条件,结果为真,由于此时KeyPart1是T的键。KeyPart1如今是a,并且T确实具备属性a:

typeNestedObject={a:{b:{c:number;d:number;};e:number;};f:number;};

继续计算条件,如今位于外部true分支中。这构建了一个新类型,它是其他两种类型的交加。第一种类型是在T上经常使用Omit适用程序类型来省略可调配给KeyPart1的字段(在本例中为a字段)的结果。第二种类型是经过递归调用NestedOmit构建的新类型。

假设对NestedOmit启动下一步求值,关于第一次性递归调用,交加类型会构建一个类型以用作a字段的类型。这将从新创立a字段,而不须要疏忽嵌套字段。

在NestedOmit的最终计算中,第一个条件将前往false,由于传递的字符串类型是c。这种状况下,可以经常使用内置类型从对象中省略该字段。这将前往b字段的类型,即省略c的原始类型。计算到此完结,TypeScript前往了须要经常使用的新类型,省略了嵌套字段。

6.小结

本文详细解释了适用于函数、接口、类和自定义类型的泛型,还经常使用泛型创立映射类型和条件类型。这些中的每一个都使泛型成为经常使用TypeScript时的弱小工具。正确的经常使用泛型将防止一遍又一遍地重复代码,并使编写的类型愈加灵敏。


Ts高级类型(Utility Types)

学习TypeScript的过程中发现对某些UtilityTypes不是很理解,就重新在文档上系统学习了一遍,TypeScript提供了几种实用工具类型来促进常见的类型转换,这些实用程序是全局可用的。

UtilityTypes文档链接:

将泛型传入的T中所有属性转换为可选属性,返回的类型可以是T的任意子集。

源码:

keyof T 获取T中的key值组合,这里的例子T是Person相当于是name | age, in关键字遍历keyof返回值为新的类型新增了name和age属性,?操作符将所有属性定义为可选属性。

将泛型传入的T中所有属性转换为必须属性,和Partial类型相反。

源码:

和Partial相反,通过-?操作符将所以可选属性去除。

将泛型传入的T中所有属性转换为只读属性。

源码:

使用readonly关键字将所有属性变成只读属性。

创建一个对象类型,使对象的键key的类型为传入的泛型K,使对象的值value的类型为传入的泛型T。

源码:

通过传入的泛型T中选择一组属性K(字符串字面值或字符串字面值的联合)来构造类型。

源码:

规定了泛型K必须是泛型T中key的子集,上面例子中K必须是‘title’|‘description’|‘completed’

通过传入的泛型T中选择一组属性K并删除其他属性,和Pick相反。

源码:

结合了Pick和Exclude,使用Exclude排除掉T中除了包含K的所有属性,使用Pick重新创建一个新的类型。

从函数类型T的形参中使用的类型构造元组类型,可以用来获取一个函数里面参数的类型,不过注意是用元组的形式获取。

源码:

获取函数类型T返回值的类型。

源码:

TypeScript在Vue3.0的Ref类型中的实践

vue3.0中的响应式原理是基于proxy做的,而使用proxy的前提是,我们要代理的是对象而不是基本类型数据。 如果我们用如下方式定义一个响应式的数据,count的改变是无法变监听拦截到的: 这时候,就需要ref来先讲基本类型包装成{value: 基本类型数据},然后再对这个包装对象进行响应式处理。 最容易想到的实现方式如下: 简单解释下,首先定义一个Ref类型,这个类型的value可以是任意类型(any),然后再定义一个ref的函数,接受一个任意类型的参数T,再将T封装成Ref类型返回。 由上图我们可以看到,我们输入了number类型的值,由泛型的反向推到我们可以知道,返回的count是Ref<number>,所以为number类型。 考虑到ref嵌套的问题,我们期望直接通过获取,而不是。 这时候我们需要对一版本进行改进。 对于改进,首先想到的是利用extends关键字进行条件判断: 由上图可是,对于ref函数的返回时,我们进行了条件判断,如果传入的参数是Ref类型,函数将原封不动返回这个Ref类型,否则需要将T包装成Ref类型再进行返回。 这样针对ref(ref(2))这种嵌套场景,首先内层的ref(2)返回的是Ref<number>类型,然后外层的ref通过判断,将内层的Ref<number>类型直接返回。 这返回值依然形如{value:number}的对象。 而不是{value:{value:number}}。 UnwrapRef<T>类型,就是对Ref类型就行反解,其作用分为: a.如果泛型T是Ref<R>类型,则UnwrapRef类型为的类型(R) b.如果泛型T不是Ref类型,则UnwrapRef类型为T 由上面的分析我们可以定义如下图的方式定义UnwrapRef: 由图可知 a.当T继承自Ref,我们都过索引类型ref取到了 T extends Ref<infer R> ? R : T (*) 这里用到了推断类型infer,其作用类似于正则匹配里面的捕获组,先提前捕获类型R,在后续的表达式(? R : T)中用到这个捕获类型,R可以是任意类型。 所以这个表达是*的含义就是:当T继承自Ref<R>, 则 UnwrapRef就是类型R b.当T未继承自Ref,我们通过索引类型other,直接返回类型T 另外我们还要考虑UnwrapRef<T>中的T是对象,且这个对象立包含Ref的情况, 仍然需要利用UnwrapRef进行反解: 由上图可以看出,如果T是object类型,我们通过索引类型‘object’找到类型{[K in keyof T] : UnwrapRef<T[K]>} 其中keyof操作符是TS中用来获取对象的key值的集合,这样K就表示对象T的任意key值,而UnwrapRef<T[K]>则表示对对象T的所以属性进行Ref反解。 总结知识点: 操作符2.利用extends进行条件判断类型推断4.索引类型5.泛型反向推到 参考文章by晨曦时梦见兮

免责声明:本文转载或采集自网络,版权归原作者所有。本网站刊发此文旨在传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及版权、内容等问题,请联系本网,我们将在第一时间删除。同时,本网站不对所刊发内容的准确性、真实性、完整性、及时性、原创性等进行保证,请读者仅作参考,并请自行核实相关内容。对于因使用或依赖本文内容所产生的任何直接或间接损失,本网站不承担任何责任。

标签: TypeScript

“从基础到实战运行-一文读懂-TypeScript-泛型 (从基础到实战:全面讲解补液)” 的相关文章

undefined-中-TypeScript-和-的区别-null (undefined是什么意思)

undefined-中-TypeScript-和-的区别-null (undefined是什么意思)

在TypeScript中,null和undefined是两个不凡的值,用于示意变量的缺失或未定义。虽然它们在某些状况下或许看起来相似,并且都可以示意"没有值",但它们在语义和用法上存在一些关键的区...

揭秘面向对象编程-一篇文章搞懂-TypeScript-类型系统和初级个性 (面向对象?)

揭秘面向对象编程-一篇文章搞懂-TypeScript-类型系统和初级个性 (面向对象?)

TypeScript是Script的超集,一方面给灵活类型的js参与了类型校验,另一方面裁减了js的各种配置。 原始数据类型 letstr:string='周小黑'letage:numbe...

的初级用法-TypeScript-万字详解 (初级的用英语怎么写)

的初级用法-TypeScript-万字详解 (初级的用英语怎么写)

TypeScript是一种类型安保的Script超集,除了基本类型和对象类型之外,TypeScript还提供了一些初级类型系统,使得咱们可以更好地处置复杂的数据结构和业务逻辑。本文将深化讨论...

TypeScript-的原因-在项目中使用

TypeScript-的原因-在项目中使用

随着 TypeScript 的日益普及,开发者们需要了解在下一个项目中使用它的理由。尽管它在早期应用中遇到了一些阻力,但在过去十年中,它已经迅速成为一种广泛使用的编程语言。 本文将介绍如何使...