Pārsniedziet pamata tipus. Apgūstiet uzlabotas TypeScript funkcijas, piemēram, nosacījumu tipus, šablonu literāļus un virkņu apstrādi, lai izveidotu neticami robustas un tipdrošas API. Visaptverošs ceļvedis globāliem izstrādātājiem.
TypeScript pilna potenciāla atraisīšana: padziļināts ieskats nosacījumu tipos, šablonu literāļos un uzlabotā virkņu apstrādē
Mūsdienu programmatūras izstrādes pasaulē TypeScript ir attīstījies daudz tālāk par savu sākotnējo lomu kā vienkāršs JavaScript tipu pārbaudītājs. Tas ir kļuvis par sarežģītu rīku, ko varētu raksturot kā tipu līmeņa programmēšanu. Šī paradigma ļauj izstrādātājiem rakstīt kodu, kas darbojas ar pašiem tipiem, radot dinamiskas, pašdokumentējošas un ievērojami drošas API. Šīs revolūcijas pamatā ir trīs jaudīgas funkcijas, kas darbojas kopā: nosacījumu tipi, šablonu literāļu tipi un virkne iekšēju virkņu apstrādes tipu.
Izstrādātājiem visā pasaulē, kuri vēlas paaugstināt savas TypeScript prasmes, šo koncepciju izpratne vairs nav greznība — tas ir nepieciešams, lai izveidotu mērogojamas un uzturamas lietojumprogrammas. Šis ceļvedis aizvedīs jūs dziļā ieskatā, sākot no pamatprincipiem un virzoties uz sarežģītiem, reāliem modeļiem, kas demonstrē to apvienoto spēku. Neatkarīgi no tā, vai veidojat dizaina sistēmu, tipdrošu API klientu vai sarežģītu datu apstrādes bibliotēku, šo funkciju apgūšana būtiski mainīs veidu, kā rakstāt TypeScript.
Pamats: nosacījumu tipi (`extends` ternary)
Pamatā nosacījumu tips ļauj jums izvēlēties vienu no diviem iespējamiem tipiem, pamatojoties uz tipa attiecību pārbaudi. Ja esat pazīstams ar JavaScript ternary operatoru (condition ? valueIfTrue : valueIfFalse), sintakse jums šķitīs uzreiz intuitīva:
type Result = SomeType extends OtherType ? TrueType : FalseType;
Šeit atslēgvārds extends darbojas kā mūsu nosacījums. Tas pārbauda, vai SomeType ir piešķirams OtherType. Sadalīsim to ar vienkāršu piemēru.
Pamata piemērs: tipa pārbaude
Iedomājieties, ka vēlamies izveidot tipu, kas atrisina līdz true, ja dotais tips T ir virkne, un false pretējā gadījumā.
type IsString
Pēc tam mēs varam izmantot šo tipu šādi:
type A = IsString<"hello">; // tips A ir true
type B = IsString<123>; // tips B ir false
Šis ir pamata būvēšanas bloks. Bet patiesais nosacījumu tipu spēks tiek atraisīts, apvienojumā ar atslēgvārdu infer.
`infer` spēks: tipu iegūšana no iekšienes
Atslēgvārds infer maina spēli. Tas ļauj jums deklarēt jaunu ģenerisko tipa mainīgo iekšpus extends klauzulas, efektīvi uztverot daļu no tipa, kuru pārbaudāt. Domājiet par to kā par tipu līmeņa mainīgo deklarāciju, kas iegūst savu vērtību no modeļa saskaņošanas.
Klasisks piemērs ir tipa atvēršana, kas atrodas Promise iekšpusē.
type UnwrapPromise
Analizēsim to:
T extends Promise: Tas pārbauda, vaiTirPromise. Ja tā ir, TypeScript mēģina saskaņot struktūru.infer U: Ja atbilstība ir veiksmīga, TypeScript uztver tipu, līdz kuramPromiseatrisina, un ievieto to jaunā tipa mainīgajā ar nosaukumuU.? U : T: Ja nosacījums ir patiess (TbijaPromise), iegūtais tips irU(atvērtais tips). Pretējā gadījumā iegūtais tips ir vienkārši sākotnējais tipsT.
Lietošana:
type User = { id: number; name: string; };
type UserPromise = Promise
type UnwrappedUser = UnwrapPromise
type UnwrappedNumber = UnwrapPromise
Šis modelis ir tik izplatīts, ka TypeScript ietver iebūvētus utilītu tipus, piemēram, ReturnType, kas tiek ieviests, izmantojot to pašu principu, lai iegūtu funkcijas atgriešanas tipu.
Sadaloši nosacījumu tipi: darbs ar apvienojumiem
Fascinējoša un būtiska nosacījumu tipu uzvedība ir tāda, ka tie kļūst sadaloši, ja pārbaudāmais tips ir "kails" ģenerisks tipa parametrs. Tas nozīmē, ka, ja jūs nododat tam apvienojuma tipu, nosacījums tiks piemērots katram apvienojuma dalībniekam atsevišķi, un rezultāti tiks savākti atpakaļ jaunā apvienojumā.
Apsveriet tipu, kas pārveido tipu par šī tipa masīvu:
type ToArray
Ja mēs nododam apvienojuma tipu uz ToArray:
type StrOrNumArray = ToArray
Rezultāts nav (string | number)[]. Tā kā T ir kails tipa parametrs, nosacījums tiek sadalīts:
ToArraykļūst parstring[]ToArraykļūst parnumber[]
Galīgais rezultāts ir šo atsevišķo rezultātu apvienojums: string[] | number[].
Šī sadalošā īpašība ir neticami noderīga apvienojumu filtrēšanai. Piemēram, iebūvētais utilītu tips Extract izmanto to, lai atlasītu dalībniekus no apvienojuma T, kas ir piešķirami U.
Ja jums ir jānovērš šī sadalošā uzvedība, varat ietīt tipa parametru kortežā abās extends klauzulas pusēs:
type ToArrayNonDistributive
type StrOrNumArrayUnified = ToArrayNonDistributive
Ar šo stabilo pamatu izpētīsim, kā mēs varam konstruēt dinamiskus virkņu tipus.
Dinamisku virkņu veidošana tipu līmenī: šablonu literāļu tipi
Šablonu literāļu tipi, kas tika ieviesti TypeScript 4.1 versijā, ļauj definēt tipus, kas ir veidoti kā JavaScript šablonu literāļu virknes. Tie ļauj jums konkatenēt, apvienot un ģenerēt jaunus virkņu literāļu tipus no esošajiem.
Sintakse ir tieši tāda, kādu jūs sagaidāt:
type World = "World";
type Greeting = `Hello, ${World}!`; // tips Greeting ir "Hello, World!"
Tas var šķist vienkārši, bet tā spēks slēpjas tā apvienošanā ar apvienojumiem un ģeneriskiem tipiem.
Apvienojumi un permutācijas
Ja šablonu literāļu tips ietver apvienojumu, tas paplašinās līdz jaunam apvienojumam, kas satur katru iespējamo virkņu permutāciju. Tas ir jaudīgs veids, kā ģenerēt labi definētu konstantu kopu.
Iedomājieties, ka definējat CSS margin īpašību kopu:
type Side = "top" | "right" | "bottom" | "left";
type MarginProperty = `margin-${Side}`;
Iegūtais tips MarginProperty ir:
"margin-top" | "margin-right" | "margin-bottom" | "margin-left"
Tas ir lieliski piemērots, lai izveidotu tipdrošus komponentu rekvizītus vai funkciju argumentus, kur ir atļauti tikai noteikti virkņu formāti.
Apvienošana ar ģeneriskiem tipiem
Šablonu literāļi patiesi spīd, ja tos izmanto ar ģeneriskiem tipiem. Jūs varat izveidot rūpnīcas tipus, kas ģenerē jaunus virkņu literāļu tipus, pamatojoties uz kādu ievadi.
type MakeEventListener
type UserListener = MakeEventListener<"user">; // "onUserChange"
type ProductListener = MakeEventListener<"product">; // "onProductChange"
Šis modelis ir atslēga dinamiskas, tipdrošas API izveidei. Bet ko darīt, ja mums ir jāmaina virknes reģistrs, piemēram, jāmaina "user" uz "User", lai iegūtu "onUserChange"? Tur iejaucas virkņu apstrādes tipi.
Rīkkopa: iekšējie virkņu apstrādes tipi
Lai padarītu šablonu literāļus vēl jaudīgākus, TypeScript nodrošina iebūvētu tipu kopu virkņu literāļu apstrādei. Tie ir kā utilītu funkcijas, bet tipu sistēmai.
Reģistra modifikatori: `Uppercase`, `Lowercase`, `Capitalize`, `Uncapitalize`
Šie četri tipi dara tieši to, ko norāda to nosaukumi:
Uppercase: Pārveido visu virknes tipu par lielajiem burtiem.type LOUD = Uppercase<"hello">; // "HELLO"Lowercase: Pārveido visu virknes tipu par mazajiem burtiem.type quiet = Lowercase<"WORLD">; // "world"Capitalize: Pārveido virknes tipa pirmo rakstzīmi par lielo burtu.type Proper = Capitalize<"john">; // "John"Uncapitalize: Pārveido virknes tipa pirmo rakstzīmi par mazo burtu.type variable = Uncapitalize<"PersonName">; // "personName"
Atgriezīsimies pie iepriekšējā piemēra un uzlabosim to, izmantojot Capitalize, lai ģenerētu parastos notikumu apstrādātāju nosaukumus:
type MakeEventListener
type UserListener = MakeEventListener<"user">; // "onUserChange"
type ProductListener = MakeEventListener<"product">; // "onProductChange"
Tagad mums ir visi elementi. Paskatīsimies, kā tie apvienojas, lai atrisinātu sarežģītas, reālas problēmas.
Sintēze: visu trīs apvienošana uzlabotiem modeļiem
Šeit teorija satiek praksi. Savienojot nosacījumu tipus, šablonu literāļus un virkņu apstrādi, mēs varam izveidot neticami sarežģītas un drošas tipu definīcijas.
1. modelis: pilnībā tipdrošs notikumu izstarotājs
Mērķis: Izveidot ģenerisku EventEmitter klasi ar metodēm, piemēram, on(), off() un emit(), kas ir pilnībā tipdrošas. Tas nozīmē:
- Metodēm nodotajam notikuma nosaukumam jābūt derīgam notikumam.
emit()nodotajai datu kravai jāsakrīt ar šim notikumam definēto tipu.- Atzvanīšanas funkcijai, kas nodota
on(), jāpieņem pareizais datu kravas tips šim notikumam.
Vispirms mēs definējam notikumu nosaukumu karti uz to datu kravas tipiem:
interface EventMap {
"user:created": { userId: number; name: string; };
"user:deleted": { userId: number; };
"product:added": { productId: string; price: number; };
}
Tagad mēs varam izveidot ģenerisko EventEmitter klasi. Mēs izmantosim ģenerisku parametru Events, kam jāpaplašina mūsu EventMap struktūra.
class TypedEventEmitter
private listeners: { [K in keyof Events]?: ((payload: Events[K]) => void)[] } = {};
// `on` metode izmanto ģenerisku `K`, kas ir mūsu notikumu kartes atslēga
on
if (!this.listeners[event]) {
this.listeners[event] = [];
}
this.listeners[event]?.push(callback);
}
// `emit` metode nodrošina, ka datu krava atbilst notikuma tipam
emit
this.listeners[event]?.forEach(callback => callback(payload));
}
}
Instancēsim un izmantosim to:
const appEvents = new TypedEventEmitter
// Tas ir tipdrošs. Datu krava tiek pareizi secināta kā { userId: number; name: string; }
appEvents.on("user:created", (payload) => {
console.log(`Lietotājs izveidots: ${payload.name} (ID: ${payload.userId})`);
});
// TypeScript radīs kļūdu šeit, jo "user:updated" nav atslēga EventMap
// appEvents.on("user:updated", () => {}); // Kļūda!
// TypeScript radīs kļūdu šeit, jo datu kravai trūkst rekvizīta "name"
// appEvents.emit("user:created", { userId: 123 }); // Kļūda!
Šis modelis nodrošina kompilācijas laika drošību daļai, kas tradicionāli ir ļoti dinamiska un kļūdu risks daudzos lietojumprogrammu.
2. modelis: tipdroša ceļa piekļuve ligzdotiem objektiem
Mērķis: Izveidot utilītu tipu, PathValue, kas var noteikt vērtības tipu ligzdotā objektā T, izmantojot punkta pieraksta virknes ceļu P (piemēram, "user.address.city").
Šis ir ļoti uzlabots modelis, kas demonstrē rekursīvus nosacījumu tipus.
Šeit ir ieviešana, kuru mēs sadalīsim:
type PathValue
? Key extends keyof T
? PathValue
: never
: P extends keyof T
? T[P]
: never;
Izsekojam tā loģiku ar piemēru: PathValue
- Sākotnējais zvans:
Pir"a.b.c". Tas atbilst šablonu literālim`${infer Key}.${infer Rest}`. Keytiek secināts kā"a".Resttiek secināts kā"b.c".- Pirmā rekursija: Tips pārbauda, vai
"a"ir atslēgaMyObject. Ja jā, tas rekursīvi zvanaPathValue. - Otrā rekursija: Tagad
Pir"b.c". Tas atkal atbilst šablonu literālim. Keytiek secināts kā"b".Resttiek secināts kā"c".- Tips pārbauda, vai
"b"ir atslēgaMyObject["a"]un rekursīvi zvanaPathValue. - Bāzes gadījums: Visbeidzot,
Pir"c". Tas neatbilst`${infer Key}.${infer Rest}`. Tipa loģika iekļūst otrajā nosacījumā:P extends keyof T ? T[P] : never. - Tips pārbauda, vai
"c"ir atslēgaMyObject["a"]["b"]. Ja jā, rezultāts irMyObject["a"]["b"]["c"]. Ja nē, tas irnever.
Lietošana ar palīgfunkciju:
declare function get
const myObject = {
user: {
name: "Alice",
address: {
city: "Wonderland",
zip: 12345
}
}
};
const city = get(myObject, "user.address.city"); // const city: string
const zip = get(myObject, "user.address.zip"); // const zip: number
const invalid = get(myObject, "user.email"); // const invalid: never
Šis jaudīgais tips novērš izpildlaika kļūdas no drukas kļūdām ceļos un nodrošina perfektu tipu secināšanu dziļi ligzdotām datu struktūrām, kas ir izplatīta problēma globālās lietojumprogrammās, kas apstrādā sarežģītas API atbildes.
Labākā prakse un veiktspējas apsvērumi
Tāpat kā ar jebkuru jaudīgu rīku, ir svarīgi izmantot šīs funkcijas gudri.
- Prioritizējiet lasāmību: Sarežģīti tipi var ātri kļūt nelasāmi. Sadaliet tos mazākos, labi nosauktos palīgtipos. Izmantojiet komentārus, lai izskaidrotu loģiku, tāpat kā jūs to darītu ar sarežģītu izpildlaika kodu.
- Izprotiet `never` tipu: Tips
neverir jūsu galvenais rīks kļūdu stāvokļu apstrādei un apvienojumu filtrēšanai nosacījumu tipos. Tas attēlo stāvokli, kam nekad nevajadzētu rasties. - Uzmanieties no rekursijas ierobežojumiem: TypeScript ir rekursijas dziļuma ierobežojums tipu instancēšanai. Ja jūsu tipi ir pārāk dziļi ligzdoti vai bezgalīgi rekursīvi, kompilators radīs kļūdu. Pārliecinieties, vai jūsu rekursīvajiem tipiem ir skaidrs bāzes gadījums.
- Uzraugiet IDE veiktspēju: Ārkārtīgi sarežģīti tipi dažreiz var ietekmēt TypeScript valodas servera veiktspēju, izraisot lēnāku automātisko pabeigšanu un tipu pārbaudi jūsu redaktorā. Ja rodas palēninājumi, pārbaudiet, vai sarežģītu tipu var vienkāršot vai sadalīt.
- Ziniet, kad apstāties: Šīs funkcijas ir paredzētas sarežģītu tipu drošības un izstrādātāju pieredzes problēmu risināšanai. Neizmantojiet tos, lai pārmērīgi inženierētu vienkāršus tipus. Mērķis ir uzlabot skaidrību un drošību, nevis pievienot nevajadzīgu sarežģītību.
Secinājums
Nosacījumu tipi, šablonu literāļi un virkņu apstrādes tipi nav tikai izolētas funkcijas; tie ir cieši integrēta sistēma sarežģītas loģikas veikšanai tipu līmenī. Tie ļauj mums pārsniegt vienkāršas anotācijas un izveidot sistēmas, kas dziļi apzinās savu struktūru un ierobežojumus.
Apgūstot šo trio, jūs varat:
- Izveidot pašdokumentējošas API: Paši tipi kļūst par dokumentāciju, palīdzot izstrādātājiem tos pareizi izmantot.
- Likvidēt veselas kļūdu klases: Tipu kļūdas tiek uztvertas kompilācijas laikā, nevis lietotājiem ražošanā.
- Uzlabot izstrādātāju pieredzi: Izbaudiet bagātīgu automātisko pabeigšanu un iekļautās kļūdu ziņas pat dinamiskākajām koda bāzes daļām.
Šo uzlaboto iespēju pieņemšana pārveido TypeScript no drošības tīkla par jaudīgu partneri izstrādē. Tas ļauj jums kodēt sarežģītu biznesa loģiku un nemainīgumu tieši tipu sistēmā, nodrošinot, ka jūsu lietojumprogrammas ir robustākas, uzturamākas un mērogojamākas globālai auditorijai.