Hrvatski

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

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

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.

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

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.