Rumah  >  Artikel  >  hujung hadapan web  >  Memahami literal templat dalam jenis data TypeScript

Memahami literal templat dalam jenis data TypeScript

青灯夜游
青灯夜游ke hadapan
2021-12-17 10:40:452229semak imbas

Pasukan TypeScript mengeluarkan TypeScript 4.1, yang merangkumi jenis literal templat yang berkuasa, pemetaan semula kunci jenis yang dipetakan dan jenis bersyarat rekursif. Artikel berikut akan membawa anda melalui jenis literal templat dalam TypeScript Saya harap ia akan membantu anda!

Memahami literal templat dalam jenis data TypeScript

Jenis Tersurat Templat

Jenis tersurat templat adalah berdasarkan jenis tersurat rentetan dan boleh dikembangkan kepada berbilang jenis melalui rentetan jenis kesatuan.

Mereka mempunyai sintaks yang sama seperti rentetan templat JavaScript, tetapi hanya boleh digunakan dalam operasi jenis. Apabila jenis literal templat digunakan, ia menggantikan pembolehubah dalam templat, mengembalikan literal rentetan baharu:

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

Apabila pembolehubah dalam templat ialah jenis kesatuan, setiap literal rentetan aksara yang mungkin akan diwakili :

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"

Jika berbilang pembolehubah dalam literal templat adalah jenis kesatuan, hasilnya akan didarab silang Contohnya, dalam contoh berikut, 2 2 3 Sejumlah 12. keputusan:

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"

Jika ia benar-benar jenis penyatuan rentetan yang sangat panjang, adalah disyorkan untuk menjananya terlebih dahulu. Ini masih sesuai untuk kes yang lebih pendek.

Kesatuan Rentetan dalam Jenis

Perkara yang paling berguna tentang literal templat ialah anda boleh menentukan rentetan baharu berdasarkan maklumat dalaman sesuatu jenis, jadi Mari kita ambil contoh:

mempunyai fungsi sedemikian makeWatchedObject, yang menambahkan kaedah on pada objek yang diluluskan. Dalam JavaScript, panggilannya kelihatan seperti ini: makeWatchedObject(baseObject), kami menganggap bahawa objek masuk ialah:

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

Kaedah on ini akan ditambahkan pada objek masuk ini, kaedah ini Menerima dua parameter, eventName (string jenis) dan callBack (function jenis):

// 伪代码
const result = makeWatchedObject(baseObject);
result.on(eventName, callBack);

Kami mahu eventName dalam bentuk ini: attributeInThePassedObject "Changed", contohnya Contohnya, passedObject mempunyai atribut firstName, dan eventName sepadan yang dihasilkan ialah firstNameChanged Begitu juga, lastName sepadan dengan lastNameChanged dan age sepadan dengan ageChanged.

Apabila fungsi callBack ini dipanggil:

  • hendaklah dihantar jenis nilai yang sama seperti attributeInThePassedObject. Contohnya, dalam passedObject, jenis nilai firstName ialah string dan fungsi panggil balik yang sepadan dengan acara firstNameChanged menerima nilai jenis string. Jenis nilai age ialah number dan fungsi panggil balik yang sepadan dengan acara ageChanged menerima nilai jenis number yang dihantar masuk.
  • Jenis nilai pulangan ialah jenis void.

on() Tandatangan kaedah pada mulanya kelihatan seperti ini: on(eventName: string, callBack: (newValue: any) => void). Menggunakan tandatangan sedemikian, kami tidak boleh melaksanakan kekangan yang dinyatakan di atas Dalam kes ini, kami boleh menggunakan literal templat:

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

Perhatikan bahawa dalam contoh ini, acara yang ditambahkan oleh kaedah on dinamakan <.>, bukan sahaja "firstNameChanged", dan nilai yang dihantar oleh fungsi panggil balik ialah "firstName", kami mahu kekangan itu daripada jenis newValue. Mari kita laksanakan perkara pertama dahulu. string

Dalam contoh ini, kami berharap jenis nama acara yang diluluskan ialah gabungan nama sifat objek, tetapi setiap ahli kesatuan disambungkan dengan aksara

dalam JavaScript, kita boleh Lakukan pengiraan seperti ini: Changed

Object.keys(passedObject).map(x => ${x}Changed)
Tersurat templat menyediakan operasi rentetan yang serupa:

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

declare function makeWatchedObject<Type>(obj: Type): Type & PropEventSource<Type>;
Perhatikan bahawa dalam contoh ini, kami menulis dalam literal templat Ia

, bolehkah kita menulisnya sebagai string & keyof Type? Jika kita menulis seperti ini, ralat akan dilaporkan: keyof Type

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

// Type &#39;keyof Type&#39; is not assignable to type &#39;string | number | bigint | boolean | null | undefined&#39;.
// Type &#39;string | number | symbol&#39; is not assignable to type &#39;string | number | bigint | boolean | null | undefined&#39;.
// ...
Daripada mesej ralat, kita juga boleh melihat sebab ralat itu Dalam "Keyof Operator of TypeScript Series", kita tahu bahawa

operator akan mengembalikan jenis keyof, tetapi jenis pembolehubah literal templat yang diperlukan ialah string | number | symbol Secara perbandingan, terdapat jenis simbol tambahan, jadi sebenarnya kita juga boleh menulis seperti ini: string | number | bigint | boolean | null | undefined

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

Menggunakan kaedah ini, TypeScript akan memberikan ralat apabila kami menggunakan nama acara yang salah:
type PropEventSource<Type> = {
     on(eventName: `${Extract<keyof Type, string>}Changed`, callback: (newValue: any) => void): void;
};

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 &#39;"firstName"&#39; is not assignable to parameter of type &#39;"firstNameChanged" | "lastNameChanged" | "ageChanged"&#39;.
 
// It&#39;s typo-resistant
person.on("frstNameChanged", () => {});
// Argument of type &#39;"frstNameChanged"&#39; is not assignable to parameter of type &#39;"firstNameChanged" | "lastNameChanged" | "ageChanged"&#39;.
Inferens literal templat (Inferens dengan Template Literals)

Sekarang mari kita laksanakan titik kedua Jenis nilai yang dihantar oleh fungsi panggil balik adalah sama dengan jenis nilai atribut yang sepadan. Kami kini hanya menggunakan jenis

untuk parameter

. Kunci untuk merealisasikan kekangan ini adalah dengan menggunakan fungsi generik: callBack

  • 捕获泛型函数第一个参数的字面量,生成一个字面量类型

  • 该字面量类型可以被对象属性构成的联合约束

  • 对象属性的类型可以通过索引访问获取

  • 应用此类型,确保回调函数的参数类型与对象属性的类型是同一个类型

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 文件里找到它们。

Uppercasea24091e5aa4cbf3564292ba7dbe442c1

把每个字符转为大写形式:

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"

Lowercasea24091e5aa4cbf3564292ba7dbe442c1

把每个字符转为小写形式:

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"

Capitalizea24091e5aa4cbf3564292ba7dbe442c1

把字符串的第一个字符转为大写形式:

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

Uncapitalizea24091e5aa4cbf3564292ba7dbe442c1

把字符串的第一个字符转换为小写形式:

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

【相关推荐:javascript学习教程

Atas ialah kandungan terperinci Memahami literal templat dalam jenis data TypeScript. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:segmentfault.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam