假设我们想要定义一种类型来描述 CSS padding 规则,如果你了解 TypeScript 类型别名和联合类型的话,能很容易定义出 CssPadding 类型。
创新互联是一家专注于网站设计制作、成都网站设计与策划设计,北戴河网站建设哪家好?创新互联做网站,专注于网站建设十载,网设计领域的专业建站公司;建站业务涵盖:北戴河等地区。北戴河做网站价格咨询:18982081108
type CssPadding =
| "padding-left"
| "padding-right"
| "padding-top"
| "padding-bottom";
但如果我们想要继续定义一种新的类型来描述 CSS margin 规则,你是不是立马想到与定义 CssPadding 类型一样的方式。
type MarginPadding =
| "margin-left"
| "margin-right"
| "margin-top"
| "margin-bottom";
对于以上定义的两种类型来说,虽然它们都能满足我们的需求。但在定义这两种类型的过程中,仍然存在一些重复的代码。
那么如何解决这个问题呢?这时我们可以使用 TypeScript 4.1 版本引入了新的模板字面量类型,具体的使用方式如下:
type Direction = "left" | "right" | "top" | "bottom";
type CssPadding = `padding-${Direction}`;
type MarginPadding = `margin-${Direction}`;
看完以上代码,是不是觉得简洁很多。与 JavaScript 中的模板字符串类似,模板字面量类型被括在反引号中,同时可以包含 ${T} 形式的占位符,其中类型变量 T 的类型可以是 string、number、boolean 或 bigint 类型。
模板字面量类型不仅为我们提供了连接字符串字面量的能力,而且还可以把非字符串基本类型的字面量转换为对应的字符串字面量类型。下面我们来举一些具体的例子:
type EventName= `${T}Changed`;
type Concat= `${S1}-${S2}`;
type ToString= `${T}`;
type T0 = EventName<"foo">; // 'fooChanged'
type T1 = Concat<"Hello", "World">; // 'Hello-World'
type T2 = ToString<"阿宝哥" | 666 | true | -1234n>; // "阿宝哥" | "true" | "666" | "-1234"
对于上述的例子来说,其实并不复杂。但现在问题来了,如果传入 EventName 或 Concat 工具类型的实际类型是联合类型的话,那么结果又会是怎样呢?接下来,我们来验证一下:
type T3 = EventName<"foo" | "bar" | "baz">;
// "fooChanged" | "barChanged" | "bazChanged"
type T4 = Concat<"top" | "bottom", "left" | "right">;
// "top-left" | "top-right" | "bottom-left" | "bottom-right"
为什么会生成这样的类型呢?这是因为对于模板字面量类型来说,当类型占位符的实际类型是联合类型(A |B |C)的话,就会被自动展开:
`[${A|B|C}]` => `[${A}]` | `[${B}]` | `[${C}]`
而对于包含多个类型占位符的情形,比如 Concat 工具类型。多个占位符中的联合类型解析为叉积:
`${A|B}-${C|D}` => `${A}-${C}` | `${A}-${D}` | `${B}-${C}` | `${B}-${D}`
了解完上述的运算规则,你应该就能理解生成的 T3 和 T4 类型了。
在使用模板字面量类型的过程中,我们还可以使用 TypeScript 提供的,用于处理字符串类型的内置工具类型,比如 Uppercase、Lowercase、Capitalize 和 Uncapitalize。具体的使用方式是这样的:
type GetterName= `get${Capitalize }`;
type Cases= `${Uppercase } ${Lowercase } ${Capitalize } ${Uncapitalize }`;
type T5 = GetterName<'foo'>; // "getFoo"
type T6 = Cases<'bar'>; // "BAR bar Bar bar"
其实,模板字面量类型的能力是很强大的,结合 TypeScript 的条件类型和 infer 关键字我们还可以实现类型推断。
type Direction = "left" | "right" | "top" | "bottom";
type InferRoot= T extends `${infer R}${Capitalize }` ? R : T;
type T7 = InferRoot<"marginRight">; // "margin"
type T8 = InferRoot<"paddingLeft">; // "padding"
在以上代码中,InferRoot 工具类型除了利用模板字面量类型之外,还使用了 TypeScript 条件类型和 infer。如果你对这两个知识点,还不了解的话,可以观看 “用了 TS 条件类型,同事直呼 YYDS” 和 “学会 TS infer,写起泛型真香!” 这两篇文章。
此外,TypeScript 4.1 版本允许我们使用 as 子句对映射类型中的键进行重新映射。它的语法如下:
type MappedTypeWithNewKeys= {
[K in keyof T as NewKeyType]: T[K]
// ^^^^^^^^^^^^^
// This is the new syntax!
}
其中 NewKeyType 的类型必须是 string | number | symbol 联合类型的子类型。在重新映射的过程中,结合模板字面量类型所提供的能力,我们就可以实现一些有用的工具类型。
比如,我们可以定义一个 Getters 工具类型,用于为对象类型生成对应的 Getter 类型:
type Getters= {
[K in keyof T as `get${Capitalize}`]: () => T[K]
};
interface Person {
name: string;
age: number;
location: string;
}
type LazyPerson = Getters;
// {
// getName: () => string;
// getAge: () => number;
// getLocation: () => string;
// }
在以上代码中,因为 keyof T 返回的类型可能会包含 symbol 类型,而 Capitalize 工具类型要求处理的类型需要是 string 类型的子类型,所以需要通过交叉运算符进行类型过滤。
除了实现简单的工具类型之外,我们还可以实现比较复杂的工具类型。比如,用于获取对象类型中,任意层级属性的类型。
type PropType= string extends Path
? unknown
: Path extends keyof T
? T[Path]
: Path extends `${infer K}.${infer R}`
? K extends keyof T
? PropType
: unknown
: unknown;
declare function getPropValue(
obj: T,
path: P
): PropType;
const obj = { a: { b: { c: 666, d: "阿宝哥" } } };
let a = getPropValue(obj, "a"); // { b: {c: number, d: string } }
let ab = getPropValue(obj, "a.b"); // {c: number, d: string }
let abd = getPropValue(obj, "a.b.d"); // string
在以上代码中,PropType 工具类型涉及 TypeScript 中的多个核心知识点。
网页名称:用了模板字面量类型,同事直呼太强了!
网站地址:http://www.mswzjz.cn/qtweb/news34/295534.html
攀枝花网站建设、攀枝花网站运维推广公司-贝锐智能,是专注品牌与效果的网络营销公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 贝锐智能