当前位置: 首页 > Web前端 > HTML5

TypeScript的模板字面量类型

时间:2023-04-04 22:58:24 HTML5

TypeScript的官方文档已经更新,但我能找到的中文文档还是旧版本。因此,对一些新增和修改的章节进行了翻译和整理。本文翻译自TypeScriptHandbook中的“TemplateLiteralTypes”一章。本文并没有严格按照原文翻译,部分内容也做了说明和补充。模板字面量类型(TemplateLiteralTypes)模板字面量类型基于字符串字面量类型,可以通过联合类型扩展为多个字符串。它们具有与JavaScript模板字符串相同的语法,但只能用于类型操作。当使用模板文字类型时,它会替换模板中的变量并返回一个新的字符串文字:typeWorld="world";typeGreeting=`hello${World}`;//typeGreeting="helloworld"当模板中的变量是联合类型时,将表示每个可能的字符串文字:typeEmailLocaleIDs="welcome_email"|“email_heading”;类型FooterLocaleIDs=“footer_title”|“footer_sendoff”;输入AllLocaleIDs=`${EmailLocaleIDs|FooterLocaleIDs}_id`;//输入AllLocaleIDs="welcome_email_id"|“email_heading_id”|“footer_title_id”|"footer_sendoff_id"如果模板字面量中有多个变量是union类型,结果会交叉相乘,例如下面的例子有223一共12个结果:typeAllLocaleIDs=`${EmailLocaleIDs|FooterLocaleIDs}_id`;输入Lang="en"|“贾”|“点”;typeLocaleMessageIDs=`${Lang}_${AllLocaleIDs}`;//typeLocaleMessageIDs="en_welcome_email_id"|"en_email_heading_id"|"en_footer_title_id"|“en_footer_sendoff_id”|“ja_welcome_email_id”|“ja_email_heading_id”|“ja_footer_title_id”|“ja_footer_sendoff_id”|“pt_welcome_email_id”|“pt_email_heading_id”|“pt_footer_title_id”|"pt_footer_sendoff_id"如果真的是很长的字符串拼接组合类型,建议提前生成,这个还是适合比较短的类型字符串联合类型(StringUnionsinTypes)模板字面量模板字面量最有用的地方是你可以根据的内部信息定义一个新的类型一个String类型,我们举个例子:有一个函数makeWatchedObject,它给传入的对象添加了一个on方法。在JavaScript中,它的调用是这样的:makeWatchedObject(baseObject),我们假设传入的对象是:constpassedObject={firstName:"Saoirse",lastName:"Ronan",age:26,};thison方法会被添加到传入的对象中,这个方法接受两个参数,eventName(字符串类型)和callBack(函数类型)://伪代码constresult=makeWatchedObject(baseObject);result.on(eventName,callBack);我们希望eventName是这种形式:attributeInThePassedObject+"Changed"。比如passedObject有一个属性firstName,对应的eventName是firstNameChanged。同样,lastName对应lastNameChanged,age对应ageChanged。调用此回调函数时:应传递与attributeInThePassedObject相同类型的值。比如passedObject中firstName的值类型是string,firstNameChanged事件对应的回调函数接受的是string类型的值。age的值的类型是number,ageChanged事件对应的回调函数接受的是number类型的值。返回值类型为void类型。on()方法的签名最初看起来像这样:on(eventName:string,callBack:(newValue:any)=>void)。使用这样的签名,我们无法实现上述约束。这时候我们可以使用模板字面量:constperson=makeWatchedObject({firstName:"Saoirse",lastName:"Ronan",age:26,});//makeWatchedObject已将`on`添加到匿名Objectperson.on("firstNameChanged",(newValue)=>{console.log(`firstNamewaschangedto${newValue}!`);});注意本例中,on方法添加的事件名称是“firstNameChanged”,而不仅仅是“firstName”,回调函数传入的值newValue,我们希望将其约束为字符串类型。让我们先实现第一点。在这个例子中,我们希望传入的事件名类型是对象属性名的组合,但是每个组合成员最后还是拼接了一个Changed字符。在JavaScript中,我们可以做这样的计算:Object.keys(passedObject).map(x=>${x}Changed)模板字面量提供了类似的字符串操作:typePropEventSource={on(eventName:`${string&keyofType}Changed`,callback:(newValue:any)=>void):void;};///使用'on'方法创建一个“被监视的对象”///以便您可以监视属性的更改。declarefunctionmakeWatchedObject(obj:Type):Type&PropEventSource;注意,在我们这里的例子中,我们在模板字面量中写了string&keyofType,我们可以只写keyofType吗?如果我们这样写,会报错:typePropEventSource={on(eventName:`${keyofType}Changed`,callback:(newValue:any)=>void):void;};//类型'keyofType'不可分配给类型'string|编号|二进制|布尔|空|undefined'.//输入'string|编号|symbol'不可分配给类型'string|编号|二进制|布尔|空|undefined'.//...从报错信息中,我们也可以看出报错的原因。在《TypeScript 系列之 Keyof 操作符》中,我们知道了关键of运算符将返回字符串|编号|symbol类型,但是模板字面量变量要求的类型是string|编号|二进制|布尔|空|写入:类型PropEventSource={on(eventName:`${Exclude}Changed`,callback:(newValue:any)=>void):void;};或者这样写:typePropEventSource={on(eventName:`${Extract}Changed`,callback:(newValue:any)=>void):void;};使用这个方法,当我们使用错误的事件名称更改时TypeScript会报错:constperson=makeWatchedObject({firstName:"Saoirse",lastName:"Ronan",age:26});person.on("firstNameChanged",()=>{});//防止简单的人为错误(使用键而不是事件名称)person.on("firstName",()=>{});//类型'"firstName"'的参数不可分配给类型'“名字已更改”|“lastNameChanged”|“年龄改变了”。//这是一个抗打字错误的人。on("firstNameChanged",()=>{});//'"firstNameChanged"'类型的参数是not可分配给'"firstNameChanged"类型的参数|“lastNameChanged”|“年龄改变了”。模板字面量推断(InferencewithTemplateLiterals)下面我们来实现第二点,回调函数传入的值的类型和对应的属性值的类型是一样的我们现在简单的对参数使用任意类型回调。实现这个约束的关键是使用泛型函数:捕获泛型函数第一个参数的字面值,生成一个字面量类型。文字类型可以由对象属性组成。对象属性的类型可以通过索引访问获得。Application该类型保证回调函数的参数类型与对象属性类型相同typePropEventSource={on(eventName:`${Key}Changed`,callback:(newValue:Type[Key])=>void):void;};声明函数makeWatchedObject(obj:Type):Type&PropEventSource;constperson=makeWatchedObject({firstName:"Saoirse",lastName:"Ronan",age:26});person.on("firstNameChanged",newName=>{//(参数)newName:stringconsole.log(`新名字是${newName.toUpperCase()}`);});person.on("ageChanged",newAge=>{//(parameter)newAge:numberif(newAge<0){console.warn("warning!negativeage");}})这里我们换成泛型功能。当用户使用“firstNameChanged”调用时,TypeScript将尝试推断Key的正确类型。它会匹配键和“Changed”之前的字符串,然后推导出字符串“firstName”,然后得到原始对象的firstName属性的类型,在本例中是string类型。内在字符串操作类型(IntrinsicStringManipulationTypes)一些TypeScript类型可用于字符操作。出于性能原因,这些类型内置到编译器中,您无法在.d.ts文件中找到它们。Uppercase将每个字符转换为大写:typeGreeting="Hello,world"typeShoutyGreeting=Uppercase//typeShoutyGreeting="HELLO,WORLD"typeASCIICacheKey=`ID-${Uppercase}`typeMainID=ASCIICacheKey<"my_app">//typeMainID="ID-MY_APP"Lowercase将每个字符转换为小写:typeGreeting="Hello,world"typeQuietGreeting=Lowercase//typeQuietGreeting="hello,world"typeASCIICacheKey=`id-${Lowercase}`typeMainID=ASCIICacheKey<"MY_APP">//typeMainID="id-my_app"Capitalize将字符串的第一个字符转换为大写:typeLowercaseGreeting="hello,world";typeGreeting=Capitalize;//typeGreeting="Hello,world"Uncapitalize将字符串的第一个字符转换为小写:输入UppercaseGreeting="HELLOWORLD";输入UncomfortableGreeting=Uncapitalize;//吨ypeUncomfortableGreeting="hELLOWORLD"字符操作类型的技术细节从TypeScript4.1开始,这些内置函数将直接使用JavaScript字符串运行时函数,而不是区域设置感知函数applyStringMapping(symbol:Symbol,str:string){switch(intrinsicTypeKinds.get(symbol.escapedNameasstring)){caseIntrinsicTypeKind.Uppercase:returnstr.toUpperCase();}案例IntrinsicTypeKind.Lowercase:returnstr.toLowerCase();案例IntrinsicTypeKind.Capitalize:returnstr.charAt(0).toUpperCase()+str.slice(1);案例IntrinsicTypeKind.Uncapitalize:returnstr.charAt(0).toLowerCase()+str.slice(1);}返回海峡;由文档翻译、重点难点解析、实战技巧三部分组成,涵盖入门、进阶、实战。旨在为您提供系统的TS学习教程。整个系列预计约有40篇文章。点此浏览全系列文章,建议顺便收藏本站。微信:“mqyqingfeng”,加我到世优唯一的读者群。如有错误或不准确的地方,请务必指正,万分感谢。如果你喜欢或者有启发,欢迎star,这也是对作者的鼓励。