Slovenčina

Komplexný sprievodca výkonnými mapovanými a podmienenými typmi v TypeScript, vrátane praktických príkladov a pokročilých prípadov použitia na tvorbu robustných a typovo bezpečných aplikácií.

Zvládnutie mapovaných a podmienených typov v TypeScript

TypeScript, nadmnožina JavaScriptu, ponúka výkonné funkcie na vytváranie robustných a udržiavateľných aplikácií. Medzi týmito funkciami vynikajú Mapované typy (Mapped Types) a Podmienené typy (Conditional Types) ako základné nástroje pre pokročilú manipuláciu s typmi. Tento sprievodca poskytuje komplexný prehľad týchto konceptov, skúma ich syntax, praktické aplikácie a pokročilé prípady použitia. Či už ste skúsený TypeScript vývojár alebo len začínate svoju cestu, tento článok vás vybaví znalosťami na efektívne využitie týchto funkcií.

Čo sú mapované typy?

Mapované typy umožňujú vytvárať nové typy transformáciou existujúcich. Iterujú cez vlastnosti existujúceho typu a na každú vlastnosť aplikujú transformáciu. Toto je obzvlášť užitočné pri vytváraní variácií existujúcich typov, ako napríklad nastavenie všetkých vlastností na voliteľné alebo iba na čítanie.

Základná syntax

Syntax pre mapovaný typ je nasledovná:

type NewType<T> = {
  [K in keyof T]: Transformation;
};

Praktické príklady

Nastavenie vlastností iba na čítanie

Povedzme, že máte rozhranie (interface) reprezentujúce profil používateľa:

interface UserProfile {
  name: string;
  age: number;
  email: string;
}

Môžete vytvoriť nový typ, kde sú všetky vlastnosti iba na čítanie:

type ReadOnlyUserProfile = {
  readonly [K in keyof UserProfile]: UserProfile[K];
};

Teraz bude mať ReadOnlyUserProfile rovnaké vlastnosti ako UserProfile, ale všetky budú iba na čítanie.

Nastavenie vlastností na voliteľné

Podobne môžete nastaviť všetky vlastnosti ako voliteľné:

type OptionalUserProfile = {
  [K in keyof UserProfile]?: UserProfile[K];
};

OptionalUserProfile bude mať všetky vlastnosti typu UserProfile, ale každá z nich bude voliteľná.

Úprava typov vlastností

Môžete tiež upraviť typ každej vlastnosti. Napríklad, môžete transformovať všetky vlastnosti na reťazce (string):

type StringifiedUserProfile = {
  [K in keyof UserProfile]: string;
};

V tomto prípade budú všetky vlastnosti v StringifiedUserProfile typu string.

Čo sú podmienené typy?

Podmienené typy umožňujú definovať typy, ktoré závisia od podmienky. Poskytujú spôsob, ako vyjadriť vzťahy medzi typmi na základe toho, či typ spĺňa určité obmedzenie. Je to podobné ternárnemu operátoru v JavaScripte, ale pre typy.

Základná syntax

Syntax pre podmienený typ je nasledovná:

T extends U ? X : Y

Praktické príklady

Určenie, či je typ reťazec

Vytvorme typ, ktorý vráti string, ak je vstupný typ reťazec, a number v opačnom prípade:

type StringOrNumber<T> = T extends string ? string : number;

type Result1 = StringOrNumber<string>;  // string
type Result2 = StringOrNumber<number>;  // number
type Result3 = StringOrNumber<boolean>; // number

Extrahovanie typu zo zjednotenia (Union)

Môžete použiť podmienené typy na extrahovanie špecifického typu zo zjednotenia (union type). Napríklad na extrahovanie typov, ktoré nemôžu byť null:

type NonNullable<T> = T extends null | undefined ? never : T;

type Result4 = NonNullable<string | null | undefined>; // string

Tu, ak je T rovné null alebo undefined, typ sa stane never, ktorý je potom odfiltrovaný zjednodušením zjednotených typov v TypeScript.

Odvodzovanie typov (Inferring)

Podmienené typy sa dajú použiť aj na odvodzovanie typov pomocou kľúčového slova infer. To vám umožní extrahovať typ z komplexnejšej typovej štruktúry.

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

V tomto príklade ReturnType extrahuje návratový typ funkcie. Kontroluje, či T je funkcia, ktorá prijíma akékoľvek argumenty a vracia typ R. Ak áno, vráti R; inak vráti any.

Kombinácia mapovaných a podmienených typov

Skutočná sila mapovaných a podmienených typov pochádza z ich kombinácie. To vám umožňuje vytvárať vysoko flexibilné a expresívne transformácie typov.

Príklad: Hĺbkové Readonly

Bežným prípadom použitia je vytvorenie typu, ktorý nastaví všetky vlastnosti objektu, vrátane vnorených vlastností, iba na čítanie. To sa dá dosiahnuť pomocou rekurzívneho podmieneného typu.

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

Tu DeepReadonly rekurzívne aplikuje modifikátor readonly na všetky vlastnosti a ich vnorené vlastnosti. Ak je vlastnosť objektom, rekurzívne volá DeepReadonly na tento objekt. V opačnom prípade jednoducho aplikuje modifikátor readonly na vlastnosť.

Príklad: Filtrovanie vlastností podľa typu

Povedzme, že chcete vytvoriť typ, ktorý obsahuje iba vlastnosti špecifického typu. Môžete to dosiahnuť kombináciou mapovaných a podmienených typov.

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

V tomto príklade FilterByType iteruje cez vlastnosti T a kontroluje, či typ každej vlastnosti rozširuje U. Ak áno, zahrnie vlastnosť do výsledného typu; inak ju vylúči mapovaním kľúča na never. Všimnite si použitie "as" na premapovanie kľúčov. Následne použijeme `Omit` a `keyof StringProperties` na odstránenie reťazcových vlastností z pôvodného rozhrania.

Pokročilé prípady použitia a vzory

Okrem základných príkladov sa mapované a podmienené typy dajú použiť v pokročilejších scenároch na vytváranie vysoko prispôsobiteľných a typovo bezpečných aplikácií.

Distributívne podmienené typy

Podmienené typy sú distributívne, keď je kontrolovaný typ zjednotením (union type). To znamená, že podmienka sa aplikuje na každého člena zjednotenia jednotlivo a výsledky sa potom spoja do nového zjednoteného typu.

type ToArray<T> = T extends any ? T[] : never;

type Result6 = ToArray<string | number>; // string[] | number[]

V tomto príklade sa ToArray aplikuje na každého člena zjednotenia string | number jednotlivo, čo vedie k string[] | number[]. Ak by podmienka nebola distributívna, výsledok by bol (string | number)[].

Používanie pomocných typov (Utility Types)

TypeScript poskytuje niekoľko vstavaných pomocných typov, ktoré využívajú mapované a podmienené typy. Tieto pomocné typy sa dajú použiť ako stavebné kamene pre komplexnejšie transformácie typov.

Tieto pomocné typy sú výkonné nástroje, ktoré môžu zjednodušiť komplexné manipulácie s typmi. Napríklad, môžete skombinovať Pick a Partial na vytvorenie typu, ktorý robí iba určité vlastnosti voliteľnými:

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

V tomto príklade má OptionalDescriptionProduct všetky vlastnosti produktu Product, ale vlastnosť description je voliteľná.

Používanie typov šablónových literálov (Template Literal Types)

Typy šablónových literálov vám umožňujú vytvárať typy založené na reťazcových literáloch. Môžu byť použité v kombinácii s mapovanými a podmienenými typmi na vytváranie dynamických a expresívnych transformácií typov. Napríklad, môžete vytvoriť typ, ktorý pred všetky názvy vlastností pridá špecifický reťazec:

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_">;

V tomto príklade bude mať PrefixedSettings vlastnosti data_apiUrl a data_timeout.

Najlepšie postupy a úvahy

Záver

Mapované typy a Podmienené typy sú výkonné funkcie v TypeScript, ktoré vám umožňujú vytvárať vysoko flexibilné a expresívne transformácie typov. Zvládnutím týchto konceptov môžete zlepšiť typovú bezpečnosť, udržiavateľnosť a celkovú kvalitu vašich aplikácií v TypeScript. Od jednoduchých transformácií, ako je nastavenie vlastností na voliteľné alebo iba na čítanie, až po komplexné rekurzívne transformácie a podmienenú logiku, tieto funkcie poskytujú nástroje, ktoré potrebujete na budovanie robustných a škálovateľných aplikácií. Pokračujte v skúmaní a experimentovaní s týmito funkciami, aby ste odomkli ich plný potenciál a stali sa zdatnejším vývojárom v TypeScript.

Ako pokračujete na svojej ceste s TypeScriptom, nezabudnite využívať bohatstvo dostupných zdrojov, vrátane oficiálnej dokumentácie TypeScriptu, online komunít a open-source projektov. Prijmite silu mapovaných a podmienených typov a budete dobre vybavení na zvládnutie aj tých najnáročnejších problémov súvisiacich s typmi.

Zvládnutie mapovaných a podmienených typov v TypeScript | MLOG