範本文字類型

範本文字類型建立在 字串文字類型 上,並具有透過聯集擴充為多個字串的能力。

它們與 JavaScript 中的範本文字字串 具有相同的語法,但用於類型位置。與具體文字類型一起使用時,範本文字會透過串接內容產生新的字串文字類型。

ts
type World = "world";
 
type Greeting = `hello ${World}`;
type Greeting = "hello world"
Try

當聯集用於內插位置時,類型是每個聯集成員可以表示的每個可能的字串文字的集合

ts
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"
Try

對於範本文字中的每個內插位置,聯集會交叉相乘

ts
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"
Try

我們通常建議人們對大型字串聯集使用即時產生,但這在較小的案例中很有用。

類型中的字串聯合

在範本字串中定義基於類型內部資訊的新字串時,其功能才會發揮作用。

考慮一個函式 (makeWatchedObject) 將一個名為 on() 的新函式新增到傳遞的物件。在 JavaScript 中,其呼叫可能如下所示:makeWatchedObject(baseObject)。我們可以將基礎物件想像成如下所示

ts
const passedObject = {
firstName: "Saoirse",
lastName: "Ronan",
age: 26,
};
Try

將新增到基礎物件的 on 函式預期兩個引數,一個 eventName (一個 字串) 和一個 callback (一個 函式)。

eventName 應為 attributeInThePassedObject + "Changed" 形式;因此,firstNameChanged 來自基礎物件中的屬性 firstName

callback 函式在呼叫時

  • 應傳遞與名稱 attributeInThePassedObject 相關聯的類型的值;因此,由於 firstName 被設定為 字串,因此 firstNameChanged 事件的 callback 預期在呼叫時傳遞一個 字串。類似地,與 age 相關聯的事件應預期以 數字 引數呼叫
  • 應具有 void 回傳類型 (為簡化示範)

on() 的天真函式特徵碼可能是:on(eventName: string, callback: (newValue: any) => void)。然而,在前述說明中,我們識別了我們想要在程式碼中記錄的重要類型約束。範本字串類型讓我們將這些約束帶入我們的程式碼中。

ts
const person = makeWatchedObject({
firstName: "Saoirse",
lastName: "Ronan",
age: 26,
});
 
// makeWatchedObject has added `on` to the anonymous Object
 
person.on("firstNameChanged", (newValue) => {
console.log(`firstName was changed to ${newValue}!`);
});
Try

請注意,on 會監聽事件 "firstNameChanged",而不仅仅是 "firstName"。如果我們確保符合資格的事件名稱集合受到監控物件中屬性名稱的聯集的約束,並在最後加上「Changed」,那麼我們對 on() 的天真規格可能會變得更強大。雖然我們很樂意在 JavaScript 中進行此類計算,即 Object.keys(passedObject).map(x => `${x}Changed`),但類型系統中的模板字面值提供了類似的方法來進行字串處理

ts
type PropEventSource<Type> = {
on(eventName: `${string & keyof Type}Changed`, callback: (newValue: any) => void): void;
};
 
/// Create a "watched object" with an `on` method
/// so that you can watch for changes to properties.
declare function makeWatchedObject<Type>(obj: Type): Type & PropEventSource<Type>;
Try

有了這個,我們可以建立一個在給予錯誤屬性時會出錯的東西

ts
const person = makeWatchedObject({
firstName: "Saoirse",
lastName: "Ronan",
age: 26
});
 
person.on("firstNameChanged", () => {});
 
// Prevent easy human error (using the key instead of the event name)
person.on("firstName", () => {});
Argument of type '"firstName"' is not assignable to parameter of type '"firstNameChanged" | "lastNameChanged" | "ageChanged"'.2345Argument of type '"firstName"' is not assignable to parameter of type '"firstNameChanged" | "lastNameChanged" | "ageChanged"'.
 
// It's typo-resistant
person.on("frstNameChanged", () => {});
Argument of type '"frstNameChanged"' is not assignable to parameter of type '"firstNameChanged" | "lastNameChanged" | "ageChanged"'.2345Argument of type '"frstNameChanged"' is not assignable to parameter of type '"firstNameChanged" | "lastNameChanged" | "ageChanged"'.
Try

使用模板字面值進行推論

請注意,我們並未從原始傳遞物件中提供的全部資訊中受益。給定 firstName 的變更(即 firstNameChanged 事件),我們應期望回呼會收到類型為 string 的引數。類似地,age 變更的回呼應收到 number 引數。我們天真地使用 any 來指定 callback 的引數類型。同樣地,模板字面值類型可以確保屬性的資料類型將與該屬性的回呼的第一個引數類型相同。

讓這成為可能的關鍵見解是:我們可以使用具有一般性的函式,例如

  1. 第一個引數中使用的字面值會擷取為字面值類型
  2. 該文字類型可以驗證為通用屬性中有效屬性的聯集
  3. 已驗證屬性的類型可以使用索引存取在通用結構中查詢
  4. 然後可以套用此打字資訊,以確保呼叫回函函式的引數是相同類型
ts
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");
}
})
Try

我們在此將 on 設為通用方法。

當使用者呼叫字串 "firstNameChanged" 時,TypeScript 會嘗試推論 Key 的正確類型。為此,它會將 Key"Changed" 之前的內容比對,並推論字串 "firstName"。一旦 TypeScript 找出這個字串,on 方法就可以在原始物件上擷取 firstName 的類型,在本例中為 string。類似地,當呼叫 "ageChanged" 時,TypeScript 會找出屬性 age 的類型,為 number

可以結合不同的方式來進行推論,通常用於解構字串,並以不同的方式重建它們。

內在字串處理類型

為了協助字串處理,TypeScript 包含一組可於字串處理中使用的類型。這些類型內建於編譯器中以提升效能,且無法在 TypeScript 中包含的 .d.ts 檔案中找到。

Uppercase<StringType>

將字串中的每個字元轉換為大寫版本。

範例
ts
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"
Try

Lowercase<StringType>

將字串中的每個字元轉換為小寫等效字元。

範例
ts
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"
Try

Capitalize<StringType>

將字串中的第一個字元轉換成大寫等效字元。

範例
ts
type LowercaseGreeting = "hello, world";
type Greeting = Capitalize<LowercaseGreeting>;
type Greeting = "Hello, world"
Try

Uncapitalize<StringType>

將字串中的第一個字元轉換成小寫等效字元。

範例
ts
type UppercaseGreeting = "HELLO WORLD";
type UncomfortableGreeting = Uncapitalize<UppercaseGreeting>;
type UncomfortableGreeting = "hELLO WORLD"
Try
內部字串處理類型技術詳細資料

截至 TypeScript 4.1,這些內部函式的程式碼直接使用 JavaScript 字串執行時間函式進行處理,且不具備地區設定感知能力。

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

TypeScript 文件是一個開源專案。透過 傳送 Pull Request ❤ 來協助我們改善這些頁面

此頁面的貢獻者
SHSteven Harms (6)
OTOrta Therox (4)
SGHSteven G. Harms (3)
SPSeol Park (1)
PPenchy (1)
6+

最後更新:2024 年 3 月 21 日