Rumah > Artikel > hujung hadapan web > Memahami literal templat dalam jenis data TypeScript
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!
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.
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:
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. 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 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 'on' 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 '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'. // ...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
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 '"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"'.Inferens literal templat (Inferens dengan Template Literals)
. 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
类型。
TypeScript 的一些类型可以用于字符操作,这些类型处于性能的考虑被内置在编译器中,你不能在 .d.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"
把每个字符转为小写形式:
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"
把字符串的第一个字符转为大写形式:
type LowercaseGreeting = "hello, world"; type Greeting = Capitalize<LowercaseGreeting>; // type Greeting = "Hello, world"
把字符串的第一个字符转换为小写形式:
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!