当前位置: 首页 > 科技观察

来做运动吧!深入TypeScript高级类型与类型体操

时间:2023-03-17 12:36:04 科技观察

TypeScript将类型的语法扩展到JavaScript,我们可以给变量添加类型,编译时会进行类型检查,编辑器可以做出更准确的智能提示。此外,TypeScript还支持高级类型以增加类型系统的灵活性。正如JavaScript的高阶函数是生成函数的函数,React的高阶组件是生成组件的组件,Typescript的高阶类型是生成类型的类型。TypeScript的高级类型是类型参数(也称为泛型)由类型定义的类型。它将对传入的类型参数执行一系列类型计算以生成新类型。typePick={[PinK]:T[P];};比如这个Pick是一个高级类型,它有类型参数T和K,类型参数经过一系列的类型计算逻辑类型后会返回一个新的类型。TypeScript的高级类型会根据类型参数寻找新的类型。这个过程会涉及到一系列的类型计算逻辑,称为类型体操。当然这不是一个正式的概念,而是社区里的一个笑话,因为有些类型的计算逻辑比较复杂。TypeScript的类型系统是图灵完备的,这意味着它可以描述任何可计算的逻辑。简单来说,它拥有所有必要的语法,例如循环和条件判断。既然TypeScript的类型系统如此强大,让我们做一些高级类型体操来感受一下。我们会做这些体操:使用ts类型进行加法使用ts类型生成重复N次的字符串string类型和object类型掌握这三种计算逻辑的规律,相信你的体操水平会有所提高。TypeScript类型语法基础在做体操之前,需要先过一遍TypeScript类型语法,即可以做哪些类型的计算逻辑。现在我们已经有了所有必要的语法,让我们看看如何进行循环和判断:ts-type条件判断ts-type条件判断的语法是condition?分支1:分支2。extends关键字用于判断A是否为B类型,例子中传入的类型参数T为1,为数字类型,所以最终返回为true。ts类型的循环ts类型没有循环,但是循环可以用递归来实现。我们要构造一个长度为n的数组,那么需要传入长度类型参数Len,元素类型参数Ele,构造好的数组类型参数Arr(递归)。然后类型计算逻辑就是判断Arr的长度是否为Len,如果是,则返回构造的Arr,如果不是,则为其添加一个元素,继续构造。这样,我们递归地创建了一个长度为Len的数组。ts类型的字符串操作ts支持构造新的字符串:也支持根据模式匹配取字符串的某一部分:因为str符合aaa的模式,所以可以匹配到,把正确的部分放到infer语句在局部变量的局部类型变量中,然后返回局部变量的值。ts类型对象操作ts支持对对象取属性取值:也可以创建新的对象类型:使用keyof获取obj的所有属性名,使用in遍历属性名并取对应的属性值,以及使用这些来生成新的newObj类型的对象。我们已经了解了常用的ts-type语法,包括条件判断、循环(递归实现)、字符串操作(构造字符串,取某个子串)、对象操作(构造对象,取属性值)。接下来,用这些做练习。ts型体操练习我们将体操分为3类进行练习,然后分别总结规则。数字类的类型体操Gymnastics1:实现高级类型Add,可以做数字加法。ts类型可以做数字加法吗?肯定可以,因为它是图灵完备的,即各种可计算的逻辑都可以做。那怎么办呢?数组类型可以带长度属性,不就是一个数字吗?可以通过构造一定长度的数组来实现加法。上面我们通过递归实现了构造一个新的一定长度数组的进阶类型:,...Arr]>那么你只需要构造两个不同长度的数组,然后将它们合并在一起,然后取长度即可。typeAdd=[...createArray,...createArray]['length']来测试一下:我们通过构造数组实现了加法!总结:ts的进阶类型如果想做数字运算,只能构造不同长度的数组,然后取长度,因为没有类型的加减乘除运算符。弦乐体操体操2:将弦乐重复n次。之前我们学习过字符串的结构,也就是通过${A}${B}的方法,那么就坐下来数数,判断重复的次数。计数涉及到数字运算,需要先构造数组再取长度。因此,我们需要递归构造一个数组进行计数,递归构造一个字符串,然后判断数组长度达到目标后返回构造的字符串。于是就有了Str(要重复的字符串)、Count(重复次数)、Arr(计数数组)、ResStr(构造字符串)四个类型参数:typeRepeactStr=Arr['length']extendsCount?ResStr:RepeactStr;我们递归构造对于数组和字符串,如果构造的数组长度达到Count,则返回构造的字符串ResStr,否则继续递归构造。测试一下:总结:递归构造字符串时,通过递归构造数组进行计数,直到计数满足条件,生成目标字符串。这个体操只使用了字符串的构造,并没有使用字符串通过模式匹配来获取子串。让我们再做一次体操吧。体操3:实现一个简单的JSParser,可以解析字符串add(11,22)的函数名和参数字符串。参数字符串的解析需要基于模式匹配的子字符串。这里我们需要分析函数名(functionName)、括号(brackets)、数字(num)、逗号(comma),分别实现对应的高级类型。解析函数名函数名由字母组成。我们只需要一个一个地取字符来判断它是不是一个字母。如果是,则记录该字符,然后递归地对字符串的其余部分进行相同的处理,直到不是字母为止。字符,函数名可以这样取出来。我们先定义字母的类型:typealphaChars='a'|'b'|'c'|'d'|'e'|'f'|'g'|'h'|'i'|'j'|'k'|'l'|'m'|'n'|'o'|'p'|'q'|'r'|'s'|'t'|'u'|'v'|'w'|'x'|'y'|'z'|'A'|'B'|'C'|'D'|'E'|'F'|'G'|'H'|'I'|'J'|'K'|'L'|'M'|'N'|'O'|'P'|'Q'|'R'|'S'|'T'|'U'|'V'|'W'|'X'|'Y'|'Z';还有一种存放中间结果的类型:typeTempParseResult={token:Token,rest:Rest}然后一个个取字符判断,把取到的字符构造成字符串存入中间结果:typeparseFunctionName=SourceStrextends`${inferPrefixChar}${inferRestStr}`?PrefixCharextendsalphaChars?parseFunctionName:TempParseResult:never;我们取单个字符,然后判断它是否是一个字母,如果是,则从该字符构造一个新的字符串,然后继续递归得到剩下的字符串。测试一下:满足我们的需求,我们通过模式匹配解析出函数名,得到子串。然后继续解析剩下的。解析括号的匹配也是这样,括号只有一个字符,所以不用递归取,只取一次即可。typebrackets='('|')';typeparseBrackets=SourceStrextends`${inferPrefixChar}${inferRestStr}`?PrefixCharextendsbrackets?TempParseResult:never:never;测试一下:继续解析剩下的:parse数字的解析也是一个字符一个字符,判断是否匹配,如果匹配,递归取下一个字符,直到不匹配:typenumChars='0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9';typeparseNum=SourceStrextends`${inferPrefixChar}${inferRestStr}`?PrefixCharextendsnumChars?parseNum:TempParseResult:never;测试一下:继续解析剩下的:解析逗号和括号一样,只需要取一个字符判断,否需要递归。typeparseComma=SourceStrextends`${inferPrefixChar}${inferRestStr}`?PrefixCharextends','?TempParseResult<',',RestStr>:never:never;测试一下:至此,我们完成了所有字符的解析,parsing按顺序组织即可。整体分析单个token的分析已经完成。整体分析就是整理顺序。每次分析后,将剩余的字符串传递给下一个分析逻辑。全部分析完成后,就可以得到各种信息。typeparse=parseFunctionNameextendsTempParseResult?parseBracketsextendsTempParseResult?parseNumextendsTempParseResultComest3>?parseNumextendsTempParseResult?parseBracketsextendsTempParseResult?{functionName:FunctionName,params:[Num1,Num2],}:never:从不:从不:从不:never:never;测试一下:大功告成,我们使用ts类型来实现一个简单的解析器!总结:ts类型可以通过模式匹配的方式提取子串。分离token,再拆分token,以实现对字符串的解析。完整代码如下:typenumChars='0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9';typealphaChars='a'|'b'|'c'|'d'|'e'|'f'|'g'|'h'|'i'|'j'|'k'|'l'|'m'|'n'|'o'|'p'|'q'|'r'|'s'|'t'|'u'|'v'|'w'|'x'|'y'|'z'|'A'|'B'|'C'|'D'|'E'|'F'|'G'|'H'|'I'|'J'|'K'|'L'|'M'|'N'|'O'|'P'|'Q'|'R'|'S'|'T'|'U'|'V'|'W'|'X'|'Y'|'Z';typeTempParseResult={token:Token,rest:Rest}typeparseFunctionName=SourceStrextends`${inferPrefixChar}${inferRestStr}`?PrefixCharextendsalphaChars?parseFunctionName:TempParseResult:never;typebrackets='('|')';typeparseBrackets=SourceStrextends`${inferPrefixChar}${inferRestStr}`?PrefixCharextendsbrackets?TempParseResult:never:never;typeparseNum=SourceStrextends`${inferPrefixChar}${inferRestStr}`?PrefixCharextendsnumChars?parseNum:TempParseResult:never;typeparseComma=SourceStrextends`${inferPrefixChar}${inferRestStr}`?PrefixCharextends','?TempParseResult<',',RestStr>:never:never;typeparse=parseFunctionNameextendsTempParseResult?parseBracketsextendsTempParseResult?parseNum扩展,inferRestTemp3>?parseCommaextendsTempParseResult?parseNumextendsTempParseResult?parseBracketsextendsTempParseResult?{function]名称:FunctionName,1,Nums:[2}:从不:从不:从不:从不:从不:从不;typeres=parse<'add(11,2)'>;对象类体操体操4:实现高级类型,取出对象类型中的数值属性值构造对象,取属性名和属性值的语法过程现在,只需在这里组合它:typefilterNumberProp={[KeyinkeyofT]:T[Key]extendsnumber?T[Key]:never}[keyofT];我们构造一个新的对象类型,通过keyof遍历对象的属性名,然后判断属性值,如果不是数字则返回never,然后取属性值返回never,表示属性确实不存在,才能达到过滤效果。测试一下:总结:对象类型可以通过{}构造新对象,通过[]获取属性值,通过keyof遍历属性名。结合这些语法可以实现各种对象类型的逻辑。总结TypeScript将类型的语法扩展到JavaScript,也支持高级类型生成类型。高级类型是通过带有类型参数的类型声明的类型,也称为泛型。从类型参数生成最终类型的类型计算逻辑被戏称为类型体操。TypeScript的类型系统是图灵完备的,可以描述任何可计算的逻辑:是吗?:可用于条件判断,常与extends配合使用。递归可用于实现循环。可以构造对象{},属性名keyof,属性值T[Key]可以构造字符串${a}${b},字符串模式匹配得到子串strextends${inferx}${infery}我们分别做了这几类体操:ts实现加法:通过递归构造数组然后取长度来重复字符串ts:递归构造数组进行计数,然后递归构造字符串ts实现parser:解析每一部分通过字符串模式匹配和取子串,最后结合调用ts实现Object属性过滤:通过构造对象调用,取属性名,取值。需要注意的是,number类是通过构造数组获取长度计算出来的,然后通过字符串的模式匹配获取子串。一是难度相对较大。其实各种高级类型只要熟悉ts类型的语法,把逻辑想清楚,都是可以一步步写出来的。和写JS逻辑没有本质区别,只不过是用来生成类型的逻辑。看完这些,是不是觉得高阶体操并不难呢?【小编推荐】HarmonyOS官方战略合作共建-HarmonyOS技术社区MicrosoftSQLServerSA密码找回Linux安装MySQL详细教程MicrosoftWindows11/Windows10Office新UI已面向所有用户推出:自适应浅色/深色主题,简单工具栏盘点Python读取Json文件和提取Json文件内容的四种方法,在Windows11时代难不难用?让我们谈谈丢失的软件