Istražite napredne značajke TypeScripta poput predložaka literalnih i uvjetnih tipova za pisanje izražajnijeg koda. Svladajte manipulaciju tipovima za složene scenarije.
Napredni tipovi u TypeScriptu: Svladavanje predložaka literalnih i uvjetnih tipova
Snaga TypeScripta leži u njegovom moćnom sustavu tipova. Dok su osnovni tipovi poput string, number i boolean dovoljni za mnoge scenarije, napredne značajke poput predložaka literalnih i uvjetnih tipova otvaraju novu razinu izražajnosti i sigurnosti tipova. Ovaj vodič pruža sveobuhvatan pregled ovih naprednih tipova, istražujući njihove mogućnosti i demonstrirajući praktične primjene.
Razumijevanje predložaka literalnih tipova
Predlošci literalnih tipova nadograđuju JavaScriptove predloške longitudinale, omogućujući vam definiranje tipova na temelju interpolacije nizova. To omogućuje stvaranje tipova koji predstavljaju specifične obrasce nizova, čineći vaš kod robusnijim i predvidljivijim.
Osnovna sintaksa i upotreba
Predlošci literalnih tipova koriste povratne kotacije (`) za obuhvaćanje definicije tipa, slično JavaScript predlošcima longitudinalima. Unutar povratnih kotacija možete interpolirati druge tipove pomoću sintakse ${}. Ovdje se događa čarolija – u osnovi stvarate tip koji je niz, sastavljen u vrijeme kompajlacije na temelju tipova unutar interpolacije.
type HTTPMethod = "GET" | "POST" | "PUT" | "DELETE";
type APIEndpoint = `/api/${string}`;
// Primjer upotrebe
const getEndpoint: APIEndpoint = "/api/users"; // Valjano
const postEndpoint: APIEndpoint = "/api/products/123"; // Valjano
const invalidEndpoint: APIEndpoint = "/admin/settings"; // TypeScript ovdje neće pokazati pogrešku jer `string` može biti bilo što
U ovom primjeru, APIEndpoint je tip koji predstavlja bilo koji niz koji počinje s /api/. Iako je ovaj osnovni primjer koristan, prava snaga predložaka literalnih tipova proizlazi iz kombinacije s preciznijim ograničenjima tipova.
Kombiniranje s unijskim tipovima
Predlošci literalnih tipova zaista zablistaju kada se koriste s unijskim tipovima. To vam omogućuje stvaranje tipova koji predstavljaju specifičan skup kombinacija nizova.
type HTTPMethod = "GET" | "POST" | "PUT" | "DELETE";
type APIPath = "users" | "products" | "orders";
type APIEndpoint = `/${APIPath}/${HTTPMethod}`;
// Valjani API Endpoints
const getUsers: APIEndpoint = "/users/GET";
const postProducts: APIEndpoint = "/products/POST";
// Nevaljani API Endpoints (rezultirat će greškama u TypeScriptu)
// const invalidEndpoint: APIEndpoint = "/users/PATCH"; // Greška: "/users/PATCH" nije dodjeljiv tipu "/users/GET" | "/users/POST" | "/users/PUT" | "/users/DELETE" | "/products/GET" | "/products/POST" | ... još 3 ... | "/orders/DELETE".
Sada je APIEndpoint restriktivniji tip koji dopušta samo specifične kombinacije API putanja i HTTP metoda. TypeScript će označiti sve pokušaje korištenja nevaljanih kombinacija, poboljšavajući sigurnost tipova.
Manipulacija nizovima pomoću predložaka literalnih tipova
TypeScript pruža ugrađene tipove za manipulaciju nizovima koji besprijekorno rade s predlošcima literalnih tipova. Ovi tipovi omogućuju vam transformaciju nizova u vrijeme kompajlacije.
- Uppercase: Pretvara niz u velika slova.
- Lowercase: Pretvara niz u mala slova.
- Capitalize: Kapitalizira prvo slovo niza.
- Uncapitalize: Pretvara prvo slovo niza u malo slovo.
type Greeting = "hello world";
type UppercaseGreeting = Uppercase; // "HELLO WORLD"
type LowercaseGreeting = Lowercase; // "hello world"
type CapitalizedGreeting = Capitalize; // "Hello world"
type UncapitalizedGreeting = Uncapitalize; // "hello world"
Ovi tipovi za manipulaciju nizovima posebno su korisni za automatsko generiranje tipova na temelju konvencija imenovanja. Na primjer, možete izvesti akcijske tipove iz naziva događaja ili obrnuto.
Praktične primjene predložaka literalnih tipova
- Definicija API endpointa: Kao što je prikazano gore, definiranje API endpointa s preciznim ograničenjima tipova.
- Obrada događaja: Stvaranje tipova za nazive događaja sa specifičnim prefiksima i sufiksima.
- Generiranje CSS klasa: Generiranje naziva CSS klasa na temelju naziva komponenti i stanja.
- Izgradnja upita baze podataka: Osiguravanje sigurnosti tipova pri konstruiranju upita baze podataka.
Međunarodni primjer: Formatiranje valute
Zamislite da gradite financijsku aplikaciju koja podržava više valuta. Možete koristiti predloške literalnih tipova za provođenje ispravnog formatiranja valute.
type CurrencyCode = "USD" | "EUR" | "GBP" | "JPY";
type CurrencyFormat = `${number} ${T}`;
const priceUSD: CurrencyFormat<"USD"> = "100 USD"; // Valjano
const priceEUR: CurrencyFormat<"EUR"> = "50 EUR"; // Valjano
// const priceInvalid: CurrencyFormat<"USD"> = "100 EUR"; // Greška: Tip 'string' nije dodjeljiv tipu '`${number} USD`'.
function formatCurrency(amount: number, currency: T): CurrencyFormat {
return `${amount} ${currency}`;
}
const formattedUSD = formatCurrency(250, "USD"); // Tip: "250 USD"
const formattedEUR = formatCurrency(100, "EUR"); // Tip: "100 EUR"
Ovaj primjer osigurava da se vrijednosti valuta uvijek formatiraju s ispravnim kodom valute, sprječavajući potencijalne pogreške.
Uronimo u uvjetne tipove
Uvjetni tipovi uvode logiku grananja u TypeScriptov sustav tipova, omogućujući vam definiranje tipova koji ovise o drugim tipovima. Ova značajka je nevjerojatno moćna za stvaranje visoko fleksibilnih i ponovno iskoristivih definicija tipova.
Osnovna sintaksa i upotreba
Uvjetni tipovi koriste ključnu riječ infer i ternarni operator (uvjet ? trueType : falseType) za definiranje uvjeta tipa.
type IsString = T extends string ? true : false;
type StringCheck = IsString; // type StringCheck = true
type NumberCheck = IsString; // type NumberCheck = false
U ovom primjeru, IsString je uvjetni tip koji provjerava je li T dodjeljiv tipu string. Ako jest, tip se rješava u true; inače se rješava u false.
Ključna riječ infer
Ključna riječ infer omogućuje vam izdvajanje tipa iz tipa. Ovo je posebno korisno pri radu sa složenim tipovima poput tipova funkcija ili tipova nizova.
type ReturnType any> = T extends (...args: any) => infer R ? R : any;
function add(a: number, b: number): number {
return a + b;
}
type AddReturnType = ReturnType; // type AddReturnType = number
U ovom primjeru, ReturnType izdvaja povratni tip funkcije T. Dio infer R uvjetnog tipa izvodi povratni tip i dodjeljuje ga tipskoj varijabli R. Ako T nije tip funkcije, tip se rješava u any.
Distributivni uvjetni tipovi
Uvjetni tipovi postaju distributivni kada je provjereni tip goli tipski parametar. To znači da se uvjetni tip primjenjuje na svaki član unijskog tipa zasebno.
type ToArray = T extends any ? T[] : never;
type NumberOrStringArray = ToArray; // type NumberOrStringArray = string[] | number[]
U ovom primjeru, ToArray pretvara tip T u tip niza. Budući da je T goli tipski parametar (nije omotan u drugi tip), uvjetni tip se primjenjuje na number i string zasebno, što rezultira unijom number[] i string[].
Praktične primjene uvjetnih tipova
- Ekstrahiranje povratnih tipova: Kao što je prikazano gore, ekstrahiranje povratnog tipa funkcije.
- Filtriranje tipova iz unije: Stvaranje tipa koji sadrži samo specifične tipove iz unije.
- Definiranje preopterećenih tipova funkcija: Stvaranje različitih tipova funkcija na temelju ulaznih tipova.
- Stvaranje tipskih čuvara: Definiranje funkcija koje sužavaju tip varijable.
Međunarodni primjer: Rukovanje različitim formatima datuma
Različite regije svijeta koriste različite formate datuma. Možete koristiti uvjetne tipove za rukovanje tim varijacijama.
type DateFormat = "YYYY-MM-DD" | "MM/DD/YYYY" | "DD.MM.YYYY";
type ParseDate = T extends "YYYY-MM-DD"
? { year: number; month: number; day: number; format: "YYYY-MM-DD" }
: T extends "MM/DD/YYYY"
? { month: number; day: number; year: number; format: "MM/DD/YYYY" }
: T extends "DD.MM.YYYY"
? { day: number; month: number; year: number; format: "DD.MM.YYYY" }
: never;
function parseDate(dateString: string, format: T): ParseDate {
// (Implementacija bi rukovala različitim formatima datuma)
if (format === "YYYY-MM-DD") {
const [year, month, day] = dateString.split("-").map(Number);
return { year, month, day, format } as ParseDate;
} else if (format === "MM/DD/YYYY") {
const [month, day, year] = dateString.split("/").map(Number);
return { month, day, year, format } as ParseDate;
} else if (format === "DD.MM.YYYY") {
const [day, month, year] = dateString.split(".").map(Number);
return { day, month, year, format } as ParseDate;
} else {
throw new Error("Nevaljan format datuma");
}
}
const parsedDateISO = parseDate("2023-10-27", "YYYY-MM-DD"); // Tip: { year: number; month: number; day: number; format: "YYYY-MM-DD"; }
const parsedDateUS = parseDate("10/27/2023", "MM/DD/YYYY"); // Tip: { month: number; day: number; year: number; format: "MM/DD/YYYY"; }
const parsedDateEU = parseDate("27.10.2023", "DD.MM.YYYY"); // Tip: { day: number; month: number; year: number; format: "DD.MM.YYYY"; }
console.log(parsedDateISO.year); // Pristupite godini znajući da će tamo biti
Ovaj primjer koristi uvjetne tipove za definiranje različitih funkcija raščlanjivanja datuma na temelju navedenog formata datuma. Tip ParseDate osigurava da vraćeni objekt ima ispravna svojstva na temelju formata.
Kombiniranje predložaka literalnih i uvjetnih tipova
Prava snaga dolazi kada kombinirate predloške literalnih i uvjetnih tipova. Ovo omogućuje nevjerojatno moćne manipulacije tipovima.
type EventName = `on${Capitalize}`;
type ExtractEventPayload = T extends EventName ? { type: T; payload: any } : never;
type ClickEvent = EventName<"click">; // "onClick"
type MouseOverEvent = EventName<"mouseOver">; // "onMouseOver"
// Primjer funkcije koja uzima tip
function processEvent(event: T): ExtractEventPayload {
// U stvarnoj implementaciji bismo zapravo poslali događaj.
console.log(`Obrada događaja ${event}`);
// U stvarnoj implementaciji, payload bi se temeljio na tipu događaja.
return { type: event, payload: {} } as ExtractEventPayload;
}
// Napominjemo da su povratni tipovi vrlo specifični:
const clickEvent = processEvent("onClick"); // { type: "onClick"; payload: any; }
const mouseOverEvent = processEvent("onMouseOver"); // { type: "onMouseOver"; payload: any; }
// Ako koristite druge nizove, dobivate `never`:
// const someOtherEvent = processEvent("someOtherEvent"); // Tip je `never`
Najbolje prakse i razmatranja
- Neka bude jednostavno: Iako moćni, ovi napredni tipovi mogu brzo postati složeni. Težite jasnoći i održivosti.
- Temeljito testirajte: Osigurajte da vaše definicije tipova rade očekivano pišući sveobuhvatne jedinice testova.
- Dokumentirajte svoj kod: Jasno dokumentirajte svrhu i ponašanje vaših naprednih tipova kako biste poboljšali čitljivost koda.
- Razmotrite performanse: Prekomjerna upotreba naprednih tipova može utjecati na vrijeme kompilacije. Profilirajte svoj kod i optimizirajte gdje je potrebno.
Zaključak
Predlošci literalnih i uvjetni tipovi moćni su alati u TypeScriptovom arsenalu. Svladavanjem ovih naprednih tipova možete pisati izražajniji, održiviji kod i kod sigurniji u pogledu tipova. Ove značajke omogućuju vam hvatanje složenih odnosa između tipova, provođenje strožih ograničenja i stvaranje visoko ponovno iskoristivih definicija tipova. Prihvatite ove tehnike kako biste podigli svoje TypeScript vještine i izgradili robusne i skalabilne aplikacije za globalnu publiku.