TypeScript细碎知识点:TS中模板字面量类型
一、什么是模板字面量类型 ( Template Literal Types )
typeScript 提供给我们的,用于字符串字面量扩展的有效工具
模板文本类型基于字符串字面量类型构建,并且能够通过联合扩展到多种字符串字面量类型。
语法:
`${T}`
🔔注意:这里的 T 是类型占位,我们不知可以传入字符串类型还可以传入非字符串的基本类型。
T 传入的基本类型,TS 会帮我们转换为字符串类型。
type A = `${1}`
// type A = "1"
type B = `${'a'}`
// type B = "a"
type C = `${true}`
// type C = "true"
type D = `${null}`
// type D = "null"
type E = `${undefined}`
// type E = "undefined"
二、模板字面量类型有什么用,用来解决什么问题?
比如说在我们平时的开发当中,有时候会用到字符串字面量类型来对一个参数做出限制:
type EmailLocale = "welcome_email" | "email_heading"; type FooterLocale = "footer_title" | "footer_sendoff";
当我们要在这些类型后面添加上id来代表它的id的字面量类型的时候,在此之前你可能会定义两个新的类型:
type EmailLocaleIDs = "welcome_email_id" | "email_heading_id"; type FooterLocaleIDs = "footer_title_id" | "footer_sendoff_id";
并且在这之后,如果有一个类型需要整合这些所有的字面量类型,你就可能需要再定义一个类型:
type AllLocaleIDs = "welcome_email_id" | "email_heading_id | footer_title_id" | "footer_sendoff_id";
在这个时候,我们就能够发现了,上面的类型当中,存在着很多的重复代码,我们是否能够将这些重复代码抽成参数,通过传参的方式来传入呢?
这样就能够在定义新的字面量类型的时候,节省下大部分的代码量了。
三、通过 JS 模板字面量来对比
对于 JS 的模板字符串,相信大部分人都有使用过,那么它和 TS 的模板字面量又有着什么不同呢
在 JS 中,我们常常会用模板字符串来拼接字符串,在早期没有模板字符串的时候,你可能会是这样凭借字符串的:
let userName = '小明' console.log('我是' + userName); //输出: ‘我是小明’
这样子不好的地方就在于,万一需要拼接的变量比较多,那么写这个拼接就会变得非常麻烦
let userName = '小明'; let age = 18 console.log('我是' + userName + '我今年' + age); //输出: 我是小明我今年18
在 JS 引入模板字符串之后,给我们提供了更加方便也更加直观的方式来拼接字符串:
let userName = "小明" let age = 18 console.log("我是" + userName + "我今年" + age) //输出: 我是小明我今年18 console.log(`我是${userName}我今年${age}`) //输出: 我是小明我今年18
那么对于 TS 来说,模板字面量的语法和 JS 的模板字符串语法是完全相同的,只不过在 TS 里,我们操作的不是值,而是类型,用于在与具体文本类型一起使用,模板文本通过连接内容来生成新的字符串文本类型。
type World = "world"; type Greeting = `hello ${World}`; //输出: "hello world"
四、模板字面量类型的作用
看完上面对于模板字面量的简单讲解,是不是发现,我们可以通过这个类型来凭借字符串字面量,从而减少重复代码
type EmailLocal = "welcome_email"; type AddId<T extends string> = `${T}_id` type EmailLocaleIDs = AddId<EmailLocal> //等价于: type EmailLocaleIDs = "welcome_email_id"
并且当你传入的类型是一个联合类型的时候,模板字面量会进行分布计算:
type EmailLocale = 'welcome_email' | 'email_heading'; type AddId<T extends string> = `${T}_id`; type EmailLocaleIDs = AddId<EmailLocale> //等价于: type EmailLocaleIDs = "welcome_email_id" | "email_heading_id"
我们还可以传入多个类型的联合类型,这样就能够解决文章刚开始说的那个问题,
type EmailLocale = "welcome_email" | "email_heading"; type FooterLocale = "footer_title" | "footer_sendoff"; type AddId<T extends string> = `${T}_id`; type EmailLocaleIDs = AddId<EmailLocale> // type EmailLocaleIDs = "welcome_email_id" | "email_heading_id" type FooterLocaleIDs = AddId<FooterLocale> // type FooterLocaleIDs = "footer_title_id" | "footer_sendoff_id" type AllLocaleIDs = AddId<EmailLocale | FooterLocale> // type AllLocaleIDs = "welcome_email_id" | "email_heading_id" | "footer_title_id" | "footer_sendoff_id"
五、结合重映射实现键值重命名
在平时的日常开发当中,可能会有一些对象,它里面保存着一些对象属性,并且我们需要对它属性进行修改的方法,比如说最常见的 get set,那么我们可能就会定义一个对象,内部存着它的属性,在定义另一个对象,用于保存get方法,再定义一个对象,用于保存set方法,这样无疑会写出很多的重复代码,
🌰举一个例子:
type User = { name: string, age: number } type GetUser = { GetName: () => string GetAge: () => number }
定义一个对象以及保存获取对象属性方法的对象,一般来说会这样子定义两个对象,这明显有着很多的重复代码:

所有我们就能够使用重映射加上模板字面量来实现一个工具类型,帮助我们快速的新建一个新对象:
type User = { name: string age: number } type Getters<T> = { [P in keyof T as `get${Capitalize<string & P>}`]: () => T[P] } type GetUser = Getters<User> // type GetUser = { // getName: () => string; // getAge: () => number; // }
浙公网安备 33010602011771号