Čeština

Komplexní průvodce výkonnými mapovanými a podmíněnými typy v TypeScriptu, včetně praktických příkladů a pokročilých případů použití pro tvorbu robustních a typově bezpečných aplikací.

Zvládnutí mapovaných a podmíněných typů v TypeScriptu

TypeScript, nadmnožina JavaScriptu, nabízí výkonné funkce pro vytváření robustních a udržovatelných aplikací. Mezi těmito funkcemi vynikají mapované typy (Mapped Types) a podmíněné typy (Conditional Types) jako zásadní nástroje pro pokročilou manipulaci s typy. Tento průvodce poskytuje komplexní přehled těchto konceptů, zkoumá jejich syntaxi, praktické aplikace a pokročilé případy použití. Ať už jste zkušený vývojář v TypeScriptu, nebo teprve začínáte, tento článek vás vybaví znalostmi pro efektivní využití těchto funkcí.

Co jsou mapované typy?

Mapované typy umožňují vytvářet nové typy transformací existujících. Iterují přes vlastnosti existujícího typu a na každou vlastnost aplikují transformaci. To je zvláště užitečné pro vytváření variant existujících typů, například pro nastavení všech vlastností jako volitelných nebo pouze pro čtení.

Základní syntaxe

Syntaxe pro mapovaný typ je následující:

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

Praktické příklady

Vytvoření vlastností pouze pro čtení

Předpokládejme, že máte rozhraní reprezentující uživatelský profil:

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

Můžete vytvořit nový typ, kde jsou všechny vlastnosti pouze pro čtení:

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

Nyní bude mít ReadOnlyUserProfile stejné vlastnosti jako UserProfile, ale všechny budou pouze pro čtení.

Vytvoření volitelných vlastností

Podobně můžete všechny vlastnosti nastavit jako volitelné:

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

OptionalUserProfile bude mít všechny vlastnosti UserProfile, ale každá z nich bude volitelná.

Modifikace typů vlastností

Můžete také modifikovat typ každé vlastnosti. Například můžete transformovat všechny vlastnosti na řetězce:

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

V tomto případě budou všechny vlastnosti v StringifiedUserProfile typu string.

Co jsou podmíněné typy?

Podmíněné typy umožňují definovat typy, které závisí na nějaké podmínce. Poskytují způsob, jak vyjádřit typové vztahy na základě toho, zda typ splňuje určité omezení. Je to podobné ternárnímu operátoru v JavaScriptu, ale pro typy.

Základní syntaxe

Syntaxe pro podmíněný typ je následující:

T extends U ? X : Y

Praktické příklady

Určení, zda je typ řetězec

Vytvořme typ, který vrátí string, pokud je vstupní typ řetězec, a number v opačném případě:

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

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

Extrakce typu ze sjednocení (union)

Můžete použít podmíněné typy k extrakci specifického typu ze sjednocení typů. Například pro extrakci typů, které nejsou null:

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

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

Zde, pokud je T null nebo undefined, typ se stane never, který je následně odfiltrován zjednodušením sjednocených typů v TypeScriptu.

Odvození typů (Inferring Types)

Podmíněné typy mohou být také použity k odvození typů pomocí klíčového slova infer. To vám umožňuje extrahovat typ z komplexnější typové struktury.

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 příkladu ReturnType extrahuje návratový typ funkce. Zkontroluje, zda je T funkce, která přijímá jakékoli argumenty a vrací typ R. Pokud ano, vrátí R; jinak vrátí any.

Kombinace mapovaných a podmíněných typů

Skutečná síla mapovaných a podmíněných typů spočívá v jejich kombinaci. To vám umožňuje vytvářet vysoce flexibilní a expresivní typové transformace.

Příklad: Deep Readonly

Běžným případem použití je vytvoření typu, který nastaví všechny vlastnosti objektu, včetně vnořených vlastností, jako pouze pro čtení. Toho lze dosáhnout pomocí rekurzivního podmíněné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>;

Zde DeepReadonly rekurzivně aplikuje modifikátor readonly na všechny vlastnosti a jejich vnořené vlastnosti. Pokud je vlastnost objekt, rekurzivně volá DeepReadonly na tento objekt. V opačném případě jednoduše aplikuje modifikátor readonly na vlastnost.

Příklad: Filtrování vlastností podle typu

Řekněme, že chcete vytvořit typ, který obsahuje pouze vlastnosti určitého typu. Toho můžete dosáhnout kombinací mapovaných a podmíněných typů.

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 příkladu FilterByType iteruje přes vlastnosti T a kontroluje, zda typ každé vlastnosti rozšiřuje U. Pokud ano, zahrne vlastnost do výsledného typu; jinak ji vyloučí mapováním klíče na never. Všimněte si použití "as" pro přemapování klíčů. Poté použijeme `Omit` a `keyof StringProperties` k odstranění řetězcových vlastností z původního rozhraní.

Pokročilé případy použití a vzory

Kromě základních příkladů lze mapované a podmíněné typy použít v pokročilejších scénářích k vytvoření vysoce přizpůsobitelných a typově bezpečných aplikací.

Distributivní podmíněné typy

Podmíněné typy jsou distributivní, když je kontrolovaný typ sjednocením typů (union type). To znamená, že podmínka je aplikována na každého člena sjednocení jednotlivě a výsledky jsou poté zkombinovány do nového sjednoceného typu.

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

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

V tomto příkladu je ToArray aplikován na každého člena sjednocení string | number jednotlivě, což vede k string[] | number[]. Pokud by podmínka nebyla distributivní, výsledek by byl (string | number)[].

Použití pomocných typů (Utility Types)

TypeScript poskytuje několik vestavěných pomocných typů, které využívají mapované a podmíněné typy. Tyto pomocné typy lze použít jako stavební bloky pro složitější typové transformace.

Tyto pomocné typy jsou výkonné nástroje, které mohou zjednodušit složité manipulace s typy. Například můžete zkombinovat Pick a Partial k vytvoření typu, který nastaví pouze určité vlastnosti jako volitelné:

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 příkladu má OptionalDescriptionProduct všechny vlastnosti Product, ale vlastnost description je volitelná.

Použití typů šablonových literálů (Template Literal Types)

Typy šablonových literálů umožňují vytvářet typy založené na řetězcových literálech. Lze je použít v kombinaci s mapovanými a podmíněnými typy k vytvoření dynamických a expresivních typových transformací. Například můžete vytvořit typ, který přidá prefix ke všem názvům vlastností:

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 příkladu bude mít PrefixedSettings vlastnosti data_apiUrl a data_timeout.

Doporučené postupy a úvahy

Závěr

Mapované typy a podmíněné typy jsou výkonné funkce v TypeScriptu, které vám umožňují vytvářet vysoce flexibilní a expresivní typové transformace. Zvládnutím těchto konceptů můžete zlepšit typovou bezpečnost, udržovatelnost a celkovou kvalitu vašich aplikací v TypeScriptu. Od jednoduchých transformací, jako je nastavení vlastností jako volitelných nebo pouze pro čtení, až po složité rekurzivní transformace a podmíněnou logiku, tyto funkce poskytují nástroje, které potřebujete k vytváření robustních a škálovatelných aplikací. Pokračujte v prozkoumávání a experimentování s těmito funkcemi, abyste odemkli jejich plný potenciál a stali se zdatnějším vývojářem v TypeScriptu.

Jak pokračujete na své cestě s TypeScriptem, nezapomeňte využívat bohatství dostupných zdrojů, včetně oficiální dokumentace TypeScriptu, online komunit a open-source projektů. Přijměte sílu mapovaných a podmíněných typů a budete dobře vybaveni k řešení i těch nejnáročnějších problémů souvisejících s typy.