中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

TypeScript數據類型中模板字面量的示例分析

發布時間:2021-12-19 11:50:36 來源:億速云 閱讀:318 作者:小新 欄目:web開發

這篇文章主要為大家展示了“TypeScript數據類型中模板字面量的示例分析”,內容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領大家一起研究并學習一下“TypeScript數據類型中模板字面量的示例分析”這篇文章吧。

模板字面量類型(Template Literal Types)

模板字面量類型以字符串字面量類型為基礎,可以通過聯合類型擴展成多個字符串。

它們跟 JavaScript 的模板字符串是相同的語法,但是只能用在類型操作中。當使用模板字面量類型時,它會替換模板中的變量,返回一個新的字符串字面量:

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

當模板中的變量是一個聯合類型時,每一個可能的字符串字面量都會被表示:

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"

如果模板字面量里的多個變量都是聯合類型,結果會交叉相乘,比如下面的例子就有 2 2 3 一共 12 種結果:

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"

如果真的是非常長的字符串聯合類型,推薦提前生成,這種還是適用于短一些的情況。

類型中的字符串聯合類型(String Unions in Types)

模板字面量最有用的地方在于你可以基于一個類型內部的信息,定義一個新的字符串,讓我們舉個例子:

有這樣一個函數 makeWatchedObject, 它會給傳入的對象添加了一個 on  方法。在 JavaScript 中,它的調用看起來是這樣:makeWatchedObject(baseObject),我們假設這個傳入對象為:

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

這個 on 方法會被添加到這個傳入對象上,該方法接受兩個參數,eventNamestring 類型) 和 callBackfunction 類型):

// 偽代碼
const result = makeWatchedObject(baseObject);
result.on(eventName, callBack);

我們希望 eventName 是這種形式:attributeInThePassedObject + "Changed" ,舉個例子,passedObject 有一個屬性 firstName,對應產生的 eventNamefirstNameChanged,同理,lastName 對應的是 lastNameChangedage 對應的是 ageChanged

當這個 callBack 函數被調用的時候:

  • 應該被傳入與 attributeInThePassedObject 相同類型的值。比如 passedObject 中, firstName 的值的類型為 string , 對應 firstNameChanged 事件的回調函數,則接受傳入一個 string  類型的值。age 的值的類型為 number,對應 ageChanged 事件的回調函數,則接受傳入一個 number 類型的值。

  • 返回值類型為 void 類型。

on() 方法的簽名最一開始是這樣的:on(eventName: string, callBack: (newValue: any) => void)。 使用這樣的簽名,我們是不能實現上面所說的這些約束的,這個時候就可以使用模板字面量:

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}!`);
});

注意這個例子里,on 方法添加的事件名為 "firstNameChanged", 而不僅僅是 "firstName",而回調函數傳入的值 newValue ,我們希望約束為 string 類型。我們先實現第一點。

在這個例子里,我們希望傳入的事件名的類型,是對象屬性名的聯合,只是每個聯合成員都還在最后拼接一個 Changed 字符,在 JavaScript 中,我們可以做這樣一個計算:

Object.keys(passedObject).map(x => ${x}Changed)

模板字面量提供了一個相似的字符串操作:

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

注意,我們在這里例子中,模板字面量里我們寫的是 string & keyof Type,我們可不可以只寫成 keyof Type 呢?如果我們這樣寫,會報錯:

type PropEventSource<Type> = {
    on(eventName: `${keyof Type}Changed`, callback: (newValue: any) => void): void;
};

// Type 'keyof Type' is not assignable to type 'string | number | bigint | boolean | null | undefined'.
// Type 'string | number | symbol' is not assignable to type 'string | number | bigint | boolean | null | undefined'.
// ...

從報錯信息中,我們也可以看出報錯原因,在 《TypeScript 系列之 Keyof 操作符》里,我們知道 keyof 操作符會返回 string | number | symbol 類型,但是模板字面量的變量要求的類型卻是 string | number | bigint | boolean | null | undefined,比較一下,多了一個 symbol 類型,所以其實我們也可以這樣寫:

type PropEventSource<Type> = {
    on(eventName: `${Exclude<keyof Type, symbol>}Changed`, callback: (newValue: any) => void): void;
};

再或者這樣寫:

type PropEventSource<Type> = {
     on(eventName: `${Extract<keyof Type, string>}Changed`, callback: (newValue: any) => void): void;
};

使用這種方式,在我們使用錯誤的事件名時,TypeScript 會給出報錯:

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"'.
 
// It's typo-resistant
person.on("frstNameChanged", () => {});
// Argument of type '"frstNameChanged"' is not assignable to parameter of type '"firstNameChanged" | "lastNameChanged" | "ageChanged"'.

模板字面量的推斷(Inference with Template Literals)

現在我們來實現第二點,回調函數傳入的值的類型與對應的屬性值的類型相同。我們現在只是簡單的對 callBack 的參數使用 any 類型。實現這個約束的關鍵在于借助泛型函數:

  • 捕獲泛型函數第一個參數的字面量,生成一個字面量類型

  • 該字面量類型可以被對象屬性構成的聯合約束

  • 對象屬性的類型可以通過索引訪問獲取

  • 應用此類型,確保回調函數的參數類型與對象屬性的類型是同一個類型

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

這里我們把 on 改成了一個泛型函數。

當一個用戶調用的時候傳入 "firstNameChanged",TypeScript 會嘗試著推斷 Key 正確的類型。它會匹配 key"Changed" 前的字符串 ,然后推斷出字符串 "firstName" ,然后再獲取原始對象的 firstName 屬性的類型,在這個例子中,就是 string 類型。

內置字符操作類型(Intrinsic String Manipulation Types)

TypeScript 的一些類型可以用于字符操作,這些類型處于性能的考慮被內置在編譯器中,你不能在 .d.ts 文件里找到它們。

Uppercase<StringType>

把每個字符轉為大寫形式:

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"

Lowercase<StringType>

把每個字符轉為小寫形式:

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"

Capitalize<StringType>

把字符串的第一個字符轉為大寫形式:

type LowercaseGreeting = "hello, world";
type Greeting = Capitalize<LowercaseGreeting>;
// type Greeting = "Hello, world"

Uncapitalize<StringType>

把字符串的第一個字符轉換為小寫形式:

type UppercaseGreeting = "HELLO WORLD";
type UncomfortableGreeting = Uncapitalize<UppercaseGreeting>;           
// type UncomfortableGreeting = "hELLO WORLD"

字符操作類型的技術細節

從 TypeScript 4.1 起,這些內置函數會直接使用 JavaScript 字符串運行時函數,而不是本地化識別 (locale aware)。

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數據類型中模板字面量的示例分析”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

曲阜市| 永城市| 白城市| 雅江县| 泸西县| 曲沃县| 乳山市| 曲周县| 嵩明县| 隆化县| 东阿县| 江华| 潞城市| 电白县| 拜城县| 双鸭山市| 莱州市| 当阳市| 彝良县| 丁青县| 庄浪县| 西和县| 楚雄市| 疏附县| 丹巴县| 来宾市| 缙云县| 务川| 蓬安县| 淮安市| 淳安县| 墨江| 祁阳县| 成安县| 镇坪县| 常州市| 筠连县| 万全县| 江阴市| 弥勒县| 伊通|