新聞中心
假設(shè)我們想要定義一種類型來描述 CSS padding 規(guī)則,如果你了解 TypeScript 類型別名和聯(lián)合類型的話,能很容易定義出 CssPadding 類型。

創(chuàng)新互聯(lián)是一家專注于網(wǎng)站設(shè)計制作、成都網(wǎng)站設(shè)計與策劃設(shè)計,北戴河網(wǎng)站建設(shè)哪家好?創(chuàng)新互聯(lián)做網(wǎng)站,專注于網(wǎng)站建設(shè)十載,網(wǎng)設(shè)計領(lǐng)域的專業(yè)建站公司;建站業(yè)務(wù)涵蓋:北戴河等地區(qū)。北戴河做網(wǎng)站價格咨詢:18982081108
type CssPadding =
| "padding-left"
| "padding-right"
| "padding-top"
| "padding-bottom";
但如果我們想要繼續(xù)定義一種新的類型來描述 CSS margin 規(guī)則,你是不是立馬想到與定義 CssPadding 類型一樣的方式。
type MarginPadding =
| "margin-left"
| "margin-right"
| "margin-top"
| "margin-bottom";
對于以上定義的兩種類型來說,雖然它們都能滿足我們的需求。但在定義這兩種類型的過程中,仍然存在一些重復(fù)的代碼。
那么如何解決這個問題呢?這時我們可以使用 TypeScript 4.1 版本引入了新的模板字面量類型,具體的使用方式如下:
type Direction = "left" | "right" | "top" | "bottom";
type CssPadding = `padding-${Direction}`;
type MarginPadding = `margin-${Direction}`;
看完以上代碼,是不是覺得簡潔很多。與 JavaScript 中的模板字符串類似,模板字面量類型被括在反引號中,同時可以包含 ${T}? 形式的占位符,其中類型變量 T 的類型可以是 string、number、boolean? 或 bigint 類型。
模板字面量類型不僅為我們提供了連接字符串字面量的能力,而且還可以把非字符串基本類型的字面量轉(zhuǎn)換為對應(yīng)的字符串字面量類型。下面我們來舉一些具體的例子:
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"
對于上述的例子來說,其實并不復(fù)雜。但現(xiàn)在問題來了,如果傳入 EventName 或 Concat 工具類型的實際類型是聯(lián)合類型的話,那么結(jié)果又會是怎樣呢?接下來,我們來驗證一下:
type T3 = EventName<"foo" | "bar" | "baz">;
// "fooChanged" | "barChanged" | "bazChanged"
type T4 = Concat<"top" | "bottom", "left" | "right">;
// "top-left" | "top-right" | "bottom-left" | "bottom-right"
為什么會生成這樣的類型呢?這是因為對于模板字面量類型來說,當(dāng)類型占位符的實際類型是聯(lián)合類型(A |B |C)的話,就會被自動展開:
`[${A|B|C}]` => `[${A}]` | `[${B}]` | `[${C}]`而對于包含多個類型占位符的情形,比如 Concat 工具類型。多個占位符中的聯(lián)合類型解析為叉積:
`${A|B}-${C|D}` => `${A}-${C}` | `${A}-${D}` | `${B}-${C}` | `${B}-${D}`了解完上述的運(yùn)算規(guī)則,你應(yīng)該就能理解生成的 T3 和 T4 類型了。
在使用模板字面量類型的過程中,我們還可以使用 TypeScript 提供的,用于處理字符串類型的內(nèi)置工具類型,比如 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"
其實,模板字面量類型的能力是很強(qiáng)大的,結(jié)合 TypeScript 的條件類型和 infer 關(guān)鍵字我們還可以實現(xiàn)類型推斷。
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?” 和 “學(xué)會 TS infer,寫起泛型真香!” 這兩篇文章。
此外,TypeScript 4.1 版本允許我們使用 as 子句對映射類型中的鍵進(jìn)行重新映射。它的語法如下:
type MappedTypeWithNewKeys= {
[K in keyof T as NewKeyType]: T[K]
// ^^^^^^^^^^^^^
// This is the new syntax!
}
其中 NewKeyType 的類型必須是 string | number | symbol 聯(lián)合類型的子類型。在重新映射的過程中,結(jié)合模板字面量類型所提供的能力,我們就可以實現(xiàn)一些有用的工具類型。
比如,我們可以定義一個 Getters 工具類型,用于為對象類型生成對應(yīng)的 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 類型的子類型,所以需要通過交叉運(yùn)算符進(jìn)行類型過濾。
除了實現(xiàn)簡單的工具類型之外,我們還可以實現(xiàn)比較復(fù)雜的工具類型。比如,用于獲取對象類型中,任意層級屬性的類型。
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 中的多個核心知識點。
標(biāo)題名稱:用了模板字面量類型,同事直呼太強(qiáng)了!
標(biāo)題鏈接:http://www.dlmjj.cn/article/cdjejhg.html


咨詢
建站咨詢
