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;
// }
 

posted on 2024-07-08 15:11  梁飞宇  阅读(68)  评论(0)    收藏  举报