Sveobuhvatan vodič za moćne TypeScript mapirane i uvjetne tipove, uključujući praktične primjere i napredne slučajeve upotrebe za izradu robusnih i tipski sigurnih aplikacija.
Ovladavanje TypeScript mapiranim i uvjetnim tipovima
TypeScript, nadskup JavaScripta, nudi moćne značajke za izradu robusnih aplikacija koje se lako održavaju. Među tim značajkama, Mapirani tipovi (Mapped Types) i Uvjetni tipovi (Conditional Types) ističu se kao ključni alati za naprednu manipulaciju tipovima. Ovaj vodič pruža sveobuhvatan pregled ovih koncepata, istražujući njihovu sintaksu, praktičnu primjenu i napredne slučajeve upotrebe. Bilo da ste iskusni TypeScript programer ili tek započinjete svoj put, ovaj će vas članak opremiti znanjem za učinkovito korištenje ovih značajki.
Što su mapirani tipovi?
Mapirani tipovi omogućuju vam stvaranje novih tipova transformacijom postojećih. Oni iteriraju kroz svojstva postojećeg tipa i primjenjuju transformaciju na svako svojstvo. Ovo je posebno korisno za stvaranje varijacija postojećih tipova, poput pretvaranja svih svojstava u opcionalna ili samo za čitanje (read-only).
Osnovna sintaksa
Sintaksa za mapirani tip je sljedeća:
type NewType<T> = {
[K in keyof T]: Transformation;
};
T
: Ulazni tip preko kojeg želite mapirati.K in keyof T
: Iterira preko svakog ključa u ulaznom tipuT
.keyof T
stvara uniju svih naziva svojstava uT
, aK
predstavlja svaki pojedinačni ključ tijekom iteracije.Transformation
: Transformacija koju želite primijeniti na svako svojstvo. To može biti dodavanje modifikatora (poputreadonly
ili?
), promjena tipa ili nešto treće.
Praktični primjeri
Pretvaranje svojstava u samo za čitanje (Read-Only)
Recimo da imate sučelje koje predstavlja korisnički profil:
interface UserProfile {
name: string;
age: number;
email: string;
}
Možete stvoriti novi tip u kojem su sva svojstva samo za čitanje:
type ReadOnlyUserProfile = {
readonly [K in keyof UserProfile]: UserProfile[K];
};
Sada će ReadOnlyUserProfile
imati ista svojstva kao UserProfile
, ali će sva biti samo za čitanje.
Pretvaranje svojstava u opcionalna
Slično tome, možete sva svojstva učiniti opcionalnima:
type OptionalUserProfile = {
[K in keyof UserProfile]?: UserProfile[K];
};
OptionalUserProfile
će imati sva svojstva od UserProfile
, ali će svako svojstvo biti opcionalno.
Modificiranje tipova svojstava
Također možete modificirati tip svakog svojstva. Na primjer, možete transformirati sva svojstva tako da budu stringovi:
type StringifiedUserProfile = {
[K in keyof UserProfile]: string;
};
U ovom slučaju, sva svojstva u StringifiedUserProfile
bit će tipa string
.
Što su uvjetni tipovi?
Uvjetni tipovi omogućuju vam definiranje tipova koji ovise o nekom uvjetu. Oni pružaju način izražavanja odnosa među tipovima ovisno o tome zadovoljava li tip određeno ograničenje. To je slično ternarnom operatoru u JavaScriptu, ali za tipove.
Osnovna sintaksa
Sintaksa za uvjetni tip je sljedeća:
T extends U ? X : Y
T
: Tip koji se provjerava.U
: Tip kojiT
proširuje (uvjet).X
: Tip koji se vraća akoT
proširujeU
(uvjet je istinit).Y
: Tip koji se vraća akoT
ne proširujeU
(uvjet je lažan).
Praktični primjeri
Utvrđivanje je li tip string
Kreirajmo tip koji vraća string
ako je ulazni tip string, a inače number
:
type StringOrNumber<T> = T extends string ? string : number;
type Result1 = StringOrNumber<string>; // string
type Result2 = StringOrNumber<number>; // number
type Result3 = StringOrNumber<boolean>; // number
Izdvajanje tipa iz unije
Možete koristiti uvjetne tipove za izdvajanje određenog tipa iz unije tipova. Na primjer, za izdvajanje tipova koji ne mogu biti null:
type NonNullable<T> = T extends null | undefined ? never : T;
type Result4 = NonNullable<string | null | undefined>; // string
Ovdje, ako je T
jednak null
ili undefined
, tip postaje never
, koji se zatim filtrira pomoću TypeScript pojednostavljenja unije tipova.
Zaključivanje (Infer) tipova
Uvjetni tipovi se također mogu koristiti za zaključivanje tipova pomoću ključne riječi infer
. To vam omogućuje izdvajanje tipa iz složenije strukture tipova.
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
function myFunction(x: number): string {
return x.toString();
}
type Result5 = ReturnType<typeof myFunction>; // string
U ovom primjeru, ReturnType
izdvaja povratni tip funkcije. Provjerava je li T
funkcija koja prima bilo koje argumente i vraća tip R
. Ako jest, vraća R
; inače, vraća any
.
Kombiniranje mapiranih i uvjetnih tipova
Prava snaga mapiranih i uvjetnih tipova dolazi iz njihove kombinacije. To vam omogućuje stvaranje vrlo fleksibilnih i izražajnih transformacija tipova.
Primjer: Dubinski Readonly
Čest slučaj upotrebe je stvaranje tipa koji sva svojstva objekta, uključujući i ugniježđena svojstva, čini samo za čitanje. To se može postići korištenjem rekurzivnog uvjetnog tipa.
type DeepReadonly<T> = {
readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K];
};
interface Company {
name: string;
address: {
street: string;
city: string;
};
}
type ReadonlyCompany = DeepReadonly<Company>;
Ovdje, DeepReadonly
rekurzivno primjenjuje readonly
modifikator na sva svojstva i njihova ugniježđena svojstva. Ako je svojstvo objekt, rekurzivno poziva DeepReadonly
na tom objektu. Inače, jednostavno primjenjuje readonly
modifikator na svojstvo.
Primjer: Filtriranje svojstava po tipu
Recimo da želite stvoriti tip koji uključuje samo svojstva određenog tipa. Možete kombinirati mapirane i uvjetne tipove kako biste to postigli.
type FilterByType<T, U> = {
[K in keyof T as T[K] extends U ? K : never]: T[K];
};
interface Person {
name: string;
age: number;
isEmployed: boolean;
}
type StringProperties = FilterByType<Person, string>; // { name: string; }
type NonStringProperties = Omit<Person, keyof StringProperties>;
U ovom primjeru, FilterByType
iterira preko svojstava od T
i provjerava proširuje li tip svakog svojstva U
. Ako da, uključuje svojstvo u rezultirajući tip; inače ga isključuje mapiranjem ključa na never
. Obratite pozornost na upotrebu "as" za remapiranje ključeva. Zatim koristimo `Omit` i `keyof StringProperties` kako bismo uklonili svojstva tipa string iz originalnog sučelja.
Napredni slučajevi upotrebe i obrasci
Osim osnovnih primjera, mapirani i uvjetni tipovi mogu se koristiti u naprednijim scenarijima za stvaranje vrlo prilagodljivih i tipski sigurnih aplikacija.
Distributivni uvjetni tipovi
Uvjetni tipovi su distributivni kada je tip koji se provjerava unija tipova. To znači da se uvjet primjenjuje na svakog člana unije pojedinačno, a rezultati se zatim kombiniraju u novu uniju tipova.
type ToArray<T> = T extends any ? T[] : never;
type Result6 = ToArray<string | number>; // string[] | number[]
U ovom primjeru, ToArray
se primjenjuje na svakog člana unije string | number
pojedinačno, što rezultira s string[] | number[]
. Da uvjet nije distributivan, rezultat bi bio (string | number)[]
.
Korištenje pomoćnih tipova (Utility Types)
TypeScript pruža nekoliko ugrađenih pomoćnih tipova koji koriste mapirane i uvjetne tipove. Ovi pomoćni tipovi mogu se koristiti kao gradivni blokovi za složenije transformacije tipova.
Partial<T>
: Čini sva svojstva odT
opcionalnima.Required<T>
: Čini sva svojstva odT
obaveznima.Readonly<T>
: Čini sva svojstva odT
samo za čitanje.Pick<T, K>
: Odabire skup svojstavaK
izT
.Omit<T, K>
: Uklanja skup svojstavaK
izT
.Record<K, T>
: Konstruira tip sa skupom svojstavaK
tipaT
.Exclude<T, U>
: Isključuje izT
sve tipove koji se mogu dodijelitiU
.Extract<T, U>
: Izdvaja izT
sve tipove koji se mogu dodijelitiU
.NonNullable<T>
: Isključujenull
iundefined
izT
.Parameters<T>
: Dohvaća parametre funkcijskog tipaT
.ReturnType<T>
: Dohvaća povratni tip funkcijskog tipaT
.InstanceType<T>
: Dohvaća tip instance konstruktorske funkcijeT
.
Ovi pomoćni tipovi moćni su alati koji mogu pojednostaviti složene manipulacije tipovima. Na primjer, možete kombinirati Pick
i Partial
kako biste stvorili tip koji samo određena svojstva čini opcionalnima:
type Optional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
interface Product {
id: number;
name: string;
price: number;
description: string;
}
type OptionalDescriptionProduct = Optional<Product, "description">;
U ovom primjeru, OptionalDescriptionProduct
ima sva svojstva od Product
, ali je svojstvo description
opcionalno.
Korištenje predložaka literalnih tipova (Template Literal Types)
Predlošci literalnih tipova omogućuju vam stvaranje tipova temeljenih na string literalima. Mogu se koristiti u kombinaciji s mapiranim i uvjetnim tipovima za stvaranje dinamičnih i izražajnih transformacija tipova. Na primjer, možete stvoriti tip koji svim nazivima svojstava dodaje određeni prefiks:
type Prefix<T, P extends string> = {
[K in keyof T as `${P}${string & K}`]: T[K];
};
interface Settings {
apiUrl: string;
timeout: number;
}
type PrefixedSettings = Prefix<Settings, "data_">;
U ovom primjeru, PrefixedSettings
će imati svojstva data_apiUrl
i data_timeout
.
Najbolje prakse i razmatranja
- Držite se jednostavnosti: Iako su mapirani i uvjetni tipovi moćni, mogu i zakomplicirati vaš kod. Pokušajte održati svoje transformacije tipova što jednostavnijima.
- Koristite pomoćne tipove: Iskoristite ugrađene pomoćne tipove TypeScripta kad god je to moguće. Dobro su testirani i mogu pojednostaviti vaš kod.
- Dokumentirajte svoje tipove: Jasno dokumentirajte svoje transformacije tipova, posebno ako su složene. To će pomoći drugim programerima da razumiju vaš kod.
- Testirajte svoje tipove: Koristite TypeScript provjeru tipova kako biste osigurali da vaše transformacije tipova rade kako se očekuje. Možete pisati jedinične testove kako biste provjerili ponašanje vaših tipova.
- Uzmite u obzir performanse: Složene transformacije tipova mogu utjecati na performanse vašeg TypeScript kompajlera. Budite svjesni složenosti svojih tipova i izbjegavajte nepotrebne izračune.
Zaključak
Mapirani tipovi i Uvjetni tipovi moćne su značajke u TypeScriptu koje vam omogućuju stvaranje vrlo fleksibilnih i izražajnih transformacija tipova. Ovladavanjem ovim konceptima možete poboljšati tipsku sigurnost, održivost i ukupnu kvalitetu svojih TypeScript aplikacija. Od jednostavnih transformacija poput pretvaranja svojstava u opcionalna ili samo za čitanje, do složenih rekurzivnih transformacija i uvjetne logike, ove značajke pružaju alate potrebne za izradu robusnih i skalabilnih aplikacija. Nastavite istraživati i eksperimentirati s ovim značajkama kako biste otključali njihov puni potencijal i postali vještiji TypeScript programer.
Dok nastavljate svoje TypeScript putovanje, ne zaboravite iskoristiti bogatstvo dostupnih resursa, uključujući službenu TypeScript dokumentaciju, online zajednice i projekte otvorenog koda. Prihvatite snagu mapiranih i uvjetnih tipova i bit ćete dobro opremljeni za rješavanje i najzahtjevnijih problema vezanih uz tipove.