首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >TypeScript 官方手册翻译计划【十一】:类型操控-模板字面量类型

TypeScript 官方手册翻译计划【十一】:类型操控-模板字面量类型

作者头像
Chor
发布于 2021-12-06 00:24:55
发布于 2021-12-06 00:24:55
98000
代码可运行
举报
文章被收录于专栏:前端之旅前端之旅
运行总次数:0
代码可运行
  • 说明:目前网上没有 TypeScript 最新官方文档的中文翻译,所以有了这么一个翻译计划。因为我也是 TypeScript 的初学者,所以无法保证翻译百分之百准确,若有错误,欢迎评论区指出;
  • 翻译内容:暂定翻译内容为 TypeScript Handbook,后续有空会补充翻译文档的其它部分;
  • 项目地址TypeScript-Doc-Zh,如果对你有帮助,可以点一个 star ~

本章节官方文档地址:Template Literal Types

模板字面量类型

模板字面量类型基于字符串字面量类型构建,可以通过联合类型拓展成多种字符串。

其语法和 JavaScript 中的模板字符串一样,但在 TypeScript 中用于表示类型。和具体的字面量类型一起使用的时候,模板字面量会通过拼接内容产生一个新的字符串字面量类型。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
type World = 'world';

type Greeting = `hello ${World}`;
	  ^
     // type Greeting = 'hello world'      

当在模板字面量的插值位置使用联合类型时,最终得到的类型是一个由联合类型每个成员可以表示的字符串字面量构成的集合:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
type EmailLocaleIDs = "welcome_email" | "email_heading";
type FooterLocaleIDs = "footer_title" | "footer_sendoff";
 
type AllLocaleIDs = `${EmailLocaleIDs | FooterLocaleIDs}_id`;
          ^
// type AllLocaleIDs = "welcome_email_id" | "email_heading_id" | "footer_title_id" | "footer_sendoff_id"

如果模板字面量有多个插值位置,那么各位置上的联合类型之间会进行叉积运算,从而得到最终的类型:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
type AllLocaleIDs = `${EmailLocaleIDs | FooterLocaleIDs}_id`;
type Lang = "en" | "ja" | "pt";
 
type LocaleMessageIDs = `${Lang}_${AllLocaleIDs}`;
           ^ 
// type LocaleMessageIDs = "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"

对于大型的字符串联合类型,我们推荐你提前生成。对于较小的字符串联合类型,则可以使用上面例子中的方法生成。

类型中的字符串联合类型

模板字面量的强大之处在于它能够基于类型中的已有信息定义一个新的字符串。

假设现在有一个 makeWatchedObject 函数,它可以给传入的对象添加一个 on 方法。在 JavaScript 中,该函数的调用形如:makeWatchedObject(baseObject)。其中,传入的对象参数类似下面这样:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const passedObject = {
    firstName: 'Saoirse',
    lastName: 'Ronan',
    age: 26,
};

即将添加给对象的 on 方法会接受两个参数,一个是 eventName(字符串),一个是 callBack(回调函数)。

eventName 的形式类似于 attributeInThePassedObject + 'Changed'。比如说,传入对象有个 firstName 属性,那么对应就会有一个叫做 firstNameChangedeventName

callBack 回调函数,在被调用的时候会:

  • 接受一个参数,参数的类型和 attributeInThePassedObject 的类型相关联。比如说,firstName 的类型是 string,那么 firstNameChanged 这个事件对应的回调函数在被调用的时候也期望接受一个 string 类型的参数。同理,和 age 相关联的事件回调函数在被调用的时候应该接受一个 number 类型的参数。
  • 返回值类型为 void(为了方便例子的讲解)

on() 的简易版函数签名可能是这样的:on(eventName: string, callBack: (newValue: any) => void)。不过,从上面的描述来看,我们发现代码中还需要实现很重要的类型约束。而模板字面量类型正好就可以帮助我们做到这一点。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const person = makeWatchedObject({
  firstName: "Saoirse",
  lastName: "Ronan",
  age: 26,
});
 
// makeWatchedObject 函数给匿名对象添加了 on 方法
 
person.on("firstNameChanged", (newValue) => {
  console.log(`firstName was changed to ${newValue}!`);
});

注意,on 监听的事件是 "firstNameChanged",而不是 "firstName"。如果我们要确保符合条件的事件名的集合受到对象属性名(末尾加上“Changed”)的联合类型的约束,那么我们的简易版 on() 方法还需要进一步完善才行。虽然在 JavaScript 中我们可以很方便地实现这个效果,比如使用 Object.keys(passedObject).map(x => ${x}Changed),不过,类型系统中的模板字面量也提供了一种类似的操控字符串的方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
type PropEventSource<Type> = {
    on(eventName: `${string & keyof Type}Changed`, callback: (newValue: any) => void): void;
};
 
// 创建一个带有 on 方法的监听对象,从而监听对象属性的变化
declare function makeWatchedObject<Type>(obj: Type): Type & PropEventSource<Type>;

这样,当传入错误的参数时,TypeScript 会抛出一个错误:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const person = makeWatchedObject({
  firstName: "Saoirse",
  lastName: "Ronan",
  age: 26
});
 
person.on("firstNameChanged", () => {});
 
// 预防常见的人为错误(错误地使用了对象的属性名而不是事件名)
person.on("firstName", () => {});
// Argument of type '"firstName"' is not assignable to parameter of type '"firstNameChanged" | "lastNameChanged" | "ageChanged"'.
 
// 拼写错误
person.on("frstNameChanged", () => {});
// Argument of type '"frstNameChanged"' is not assignable to parameter of type '"firstNameChanged" | "lastNameChanged" | "ageChanged"'.

模板字面量的推断

注意,目前为止我们还没有完全利用传入对象提供的信息。firstName 改变的时候(触发 firstNameChanged 事件),我们期望回调函数会接受一个 string 类型的参数。同理,age 改变的时候,对应的回调函数也会接受一个 number 类型的参数。但目前,我们仅仅只是用 any 作为回调函数参数的类型而已。这里我们需要再次使用模板字面量类型,它可以确保属性的数据类型和属性对应的回调函数的参数类型保持一致。

实现这一点的关键在于:我们可以使用一个带有泛型的函数,从而确保:

  1. 第一个参数中的字面量可以被捕获为一个字面量类型
  2. 泛型的有效属性会构成一个联合类型,可以验证捕获的字面量类型是否是该联合类型的一个成员
  3. 可以在泛型结构中通过按索引访问的方式去查看已验证属性的类型
  4. 该类型信息可以被进一步利用,以保证回调函数的参数也是相同的类型
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
type PropEventSource<Type> = {
    on<Key extends string & keyof Type>(eventName: `${Key}Changed`, callback: (newValue: Type[Key]) => void): void;
}

declare function makeWatchedObject<Type>(obj: Type): Type & PropEventSource<Type>;

const person = makeWatchedObject({
  firstName: "Saoirse",
  lastName: "Ronan",
  age: 26
});
 
person.on("firstNameChanged", newName => {
                                 ^
                     // (parameter) newName: string
    console.log(`new name is ${newName.toUpperCase()}`);
});
 
person.on("ageChanged", newAge => {
                           ^     
                 // (parameter) newAge: number
    if (newAge < 0) {
        console.warn("warning! negative age");
    }
})

这里我们让 on 变成了一个泛型方法。

当开发者通过字符串 "firstNameChanged" 调用了 on 方法的时候,TypeScript 会尝试推断出 Key 的正确类型。具体地说,它会将 Key"Changed" 前面的部分进行匹配,并推断出字符串 "firstName"。一旦 TypeScript 推断完成,on 方法就可以取出原对象的 firstName 属性的类型 —— 即 string 类型。同理,当通过 "ageChanged" 调用方法的时候,TypeScript 也会发现 age 属性的类型是 number

推断有多种不同的结合方式,通常用于解构字符串,并以不同的方式对字符串进行重构。

内建的字符串操控类型

为了方便操控字符串,TypeScript 引入了一些相关的类型。为了提高性能,这些类型是内建到编译器中的,并且无法在 TypeScript 附带的 .d.ts 文件中找到。

Uppercase<StringType>

将字符串中的每个字符转化为大写形式。

示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
type Greeting = 'Hello, world'
type ShoutyGreeting = Uppercase<Greeting>
		^
        // type ShoutyGreeting = 'HELLO, WORLD'    
type ASCIICacheKey<Str extends string> = `ID-${Uppercase<Str>}`
type MainID = ASCIICacheKey<'my_app'>
	  ^
	  // type MainID = 'ID-MY_APP'
Lowercase<StringType>

将字符串中的每个字符转化为小写形式。

示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
type Greeting = "Hello, world"
type QuietGreeting = Lowercase<Greeting>
          ^ 
      // type QuietGreeting = "hello, world"
 
type ASCIICacheKey<Str extends string> = `id-${Lowercase<Str>}`
type MainID = ASCIICacheKey<"MY_APP">
        ^ 
    // type MainID = "id-my_app"
Capitalize<StringType>

将字符串的首字符转化为大写形式。

示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
type LowercaseGreeting = 'hello, world';
type Greeting = Capitalize<LowercaseGreeting>;
        ^
       // type Greeting = 'Hello, world'     
Uncapitalize<StringType>

将字符串的首字符转化为小写形式。

示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
type UppercaseGreeting = 'HELLO WORLD';
type UncomfortableGreeting = Uncapitalize<UppercaseGreeting>;
		   ^	
          // type UncomfortableGreeting = "hELLO WORLD"     

关于内建字符串操控类型的一些技术细节:

从 TypeScript 4.1 开始,这些内建函数的实现直接使用了 JavaScript 的字符串运行时函数进行操作,并且无法做到本地化识别。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function applyStringMapping(symbol: Symbol, str: string) {
    switch (intrinsicTypeKinds.get(symbol.escapedName as string)) {
        case IntrinsicTypeKind.Uppercase: return str.toUpperCase();
        case IntrinsicTypeKind.Lowercase: return str.toLowerCase();
        case IntrinsicTypeKind.Capitalize: return str.charAt(0).toUpperCase() + str.slice(1);
        case IntrinsicTypeKind.Uncapitalize: return str.charAt(0).toLowerCase() + str.slice(1);
    }
    return str;
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-12-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
TypeScript系列教程九《类型转换》-- 模板文本类型
它们的语法与JavaScript中的模板文本字符串相同,但用于类型位置。当与具体的文本类型一起使用时,模板文本通过连接内容生成新的字符串文本类型。
星宇大前端
2021/09/23
7410
TypeScript系列教程九《类型转换》-- 模板文本类型
TypeScript - 字面量类型
在 TypeScript 中,字面量类型(Literal Types)是指那些与特定字面量值严格对应的类型。字面量类型包括字符串字面量类型、数字字面量类型和布尔字面量类型。使用字面量类型可以提高代码的准确性和可读性,因为它们限制变量只能赋值为特定的字面量。
前端黑板报
2024/05/21
2850
TypeScript - 字面量类型
JavaScript进阶-模板字符串与增强的对象字面量
随着ES6的推出,JavaScript语言在字符串处理和对象定义方面获得了显著的提升。模板字符串(Template Literals)和增强的对象字面量(Enhanced Object Literals)就是其中两项重要改进,它们不仅让代码更加简洁、易读,还大大增强了表达能力。本文将深入浅出地介绍这两个特性,探讨它们的使用技巧、常见问题、易错点以及如何避免这些错误,并通过实例代码加深理解。
Jimaks
2024/06/21
2360
JavaScript进阶-模板字符串与增强的对象字面量
TypeScript 官方手册翻译计划【七】:类型操控-类型操作符
本章节官方文档地址:Keyof Type Operator、Typeof Type Operator
Chor
2021/12/06
6270
TypeScript 官方手册翻译计划【十】:类型操控-映射类型
索引签名用于为那些没有提前声明的属性去声明类型,而映射类型是基于索引签名的语法构建的。
Chor
2021/12/06
8500
类型别名与字面量类型_TypeScript笔记10
这种类型完整性补充让TypeScript能够更细致地“理解”(静态分析)代码含义,进而发现一些不那么直接的潜在问题
ayqy贾杰
2019/06/12
1.3K0
Golang 字面量的表示
在 Go 语言中,字面量是用来表示固定值的表达式。Go 支持几种类型的字面量,包括整型、浮点型、字符串、字符、布尔值以及复合类型(如数组、切片、结构体、映射等)。
恋喵大鲤鱼
2024/08/31
1950
【TypeScript 演化史 -- 4】更多的字面量类型 与 内置类型声明
TypeScript 1.8 引入了字符串字面量类型,用于将变量限制为可能的字符串值的有限集。在 TypeScript 2.0 中,字面量类型不再局限于字符串。以下字面量类型已添加到类型系统中:
前端小智@大迁世界
2022/06/15
1.2K0
【TypeScript 演化史 -- 4】更多的字面量类型 与 内置类型声明
《现代Typescript高级教程》高级类型
映射类型(Mapped Types)是 TypeScript 中一种强大的类型操作工具,它允许我们在编译时转换已知类型的属性,并创建一个新的类型。通过映射类型,我们可以对已有类型的属性进行转换、修改或添加新的属性。这在许多情况下都非常有用,例如将属性变为只读或可选,从现有属性中选择一部分属性等。
linwu
2023/07/27
3380
TS的字面量类型与接口类型
炑焽
2024/08/09
2580
【前端】JavaScript中的字面量概念与应用详解
字面量(Literal) 是指代码中直接表示固定值的语法结构。例如,在代码 let number = 42; 中,42 就是一个数字字面量。字面量并不依赖于变量或函数的运行结果,而是代码中明确的 常量值。字面量可以用于多种数据类型,包括 数字、字符串、布尔值、数组、对象 等,从而涵盖了广泛的 数据表示需求。
CSDN-Z
2025/06/02
1310
【前端】JavaScript中的字面量概念与应用详解
类型即正义:TypeScript 从入门到实践(二):函数、交叉/联合类型与类型守卫
了解了基础的 TS 类型,接口之后,我们开始了解如何给更加复杂的结构注解类型,这就是我们这节里面要引出的函数,进而我们讲解如何对类型进行运算:交叉类型和联合类型,最后我们讲解了最原子类型:字面量类型,以及如何与联合类型搭配实现类型守卫效果。
一只图雀
2020/04/15
2.8K0
类型即正义:TypeScript 从入门到实践(二):函数、交叉/联合类型与类型守卫
让你的TypeScript代码更优雅,这10个特性你需要了解下
在这个技术飞速发展的时代,掌握TypeScript的这些高级功能,不仅可以让你的代码更加健壮,还能大大提升你的开发效率。赶紧来看看吧!
前端达人
2024/06/14
5980
让你的TypeScript代码更优雅,这10个特性你需要了解下
TS 进阶 - 类型编程
内置工具类型中有一个从联合类型中提出 null | undefined 的工具类型,可以借助其实现一个剔除所有属性的 null 与 undefined:
Cellinlab
2023/05/17
8250
Scala | 教程 | 学习手册 --- 字面量/值/变量和类型
但也不能赋为类型不兼容的数据。不过,如果定义类型double的var,再赋值Int值是可以的。因为Int数可以转为Double数。
曲奇
2021/12/14
7610
Scala | 教程 | 学习手册 --- 字面量/值/变量和类型
TypeScript 类型体操 - 基础操作
类型即 number、boolean、string 等基础类型和 Object、Function 等复合类型,它们是编程语言提供的对不同内容的抽象:
Cellinlab
2023/05/17
2K0
TypeScript入门秘籍:快速掌握静态类型编程
TypeScript是一种静态类型的JavaScript超集,它添加了可选的类型注解,使得代码更加健壮、易于维护。无论你是初学者还是有一定编程经验的开发者,这篇博客将带你快速入门TypeScript。
Front_Yue
2024/09/08
2770
TypeScript入门秘籍:快速掌握静态类型编程
TypeScript 官方手册翻译计划【三】:类型收缩
如果 padding 是 number 类型,那么它将作为 input 前缀空格的个数,如果它是 string 类型,那么它将直接作为 input 的前缀。现在我们尝试实现一下相关的逻辑,假定要给 padLeft 传入 number 类型的 padding 参数。
Chor
2021/11/29
2.1K0
【TypeScript】010-类型别名、字符串字面量类型、元组、枚举
上例中,我们使用 type 定了一个字符串字面量类型 EventNames,它只能取三种字符串中的一种。
訾博ZiBo
2025/01/06
1760
TypeScript学习笔记(二)—— TypeScript基础
JavaScript 的类型分为两种:原始数据类型(Primitive data types)和对象类型(Object types)。
张果
2022/10/04
5.4K0
TypeScript学习笔记(二)—— TypeScript基础
推荐阅读
相关推荐
TypeScript系列教程九《类型转换》-- 模板文本类型
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档