Magyar

Átfogó útmutató a TypeScript hatékony Mapped és Conditional típusaihoz, gyakorlati példákkal és haladó felhasználási esetekkel a robusztus és típusbiztos alkalmazások létrehozásához.

A TypeScript Mapped és Conditional típusainak mesteri szintű használata

A TypeScript, a JavaScript egy szuperhalmaza, hatékony funkciókat kínál robusztus és karbantartható alkalmazások létrehozásához. Ezen funkciók közül a Mapped Types (leképezett típusok) és a Conditional Types (feltételes típusok) kiemelkednek mint a haladó típuskészítés alapvető eszközei. Ez az útmutató átfogó áttekintést nyújt ezekről a koncepciókról, bemutatva szintaxisukat, gyakorlati alkalmazásaikat és haladó felhasználási eseteiket. Akár tapasztalt TypeScript fejlesztő, akár csak most kezdi útját, ez a cikk felvértezi Önt azzal a tudással, amellyel hatékonyan kiaknázhatja ezeket a funkciókat.

Mik azok a Mapped típusok?

A Mapped típusok lehetővé teszik új típusok létrehozását meglévők átalakításával. Végigiterálnak egy meglévő típus tulajdonságain, és minden tulajdonságra egy transzformációt alkalmaznak. Ez különösen hasznos meglévő típusok variációinak létrehozásakor, például az összes tulajdonság opcionálissá vagy írásvédetté tételénél.

Alapvető szintaxis

Egy Mapped típus szintaxisa a következő:

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

Gyakorlati példák

Tulajdonságok írásvédetté tétele

Tegyük fel, hogy van egy interfészünk, amely egy felhasználói profilt reprezentál:

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

Létrehozhat egy új típust, ahol minden tulajdonság írásvédett:

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

Most a ReadOnlyUserProfile ugyanazokkal a tulajdonságokkal rendelkezik, mint a UserProfile, de mindegyik írásvédett lesz.

Tulajdonságok opcionálissá tétele

Hasonlóképpen, minden tulajdonságot opcionálissá tehet:

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

Az OptionalUserProfile a UserProfile összes tulajdonságával rendelkezik, de mindegyik opcionális lesz.

Tulajdonságtípusok módosítása

Módosíthatja az egyes tulajdonságok típusát is. Például az összes tulajdonságot stringgé alakíthatja:

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

Ebben az esetben a StringifiedUserProfile összes tulajdonsága string típusú lesz.

Mik azok a Conditional típusok?

A Conditional típusok lehetővé teszik, hogy feltételtől függő típusokat definiáljunk. Módot biztosítanak a típuskapcsolatok kifejezésére annak alapján, hogy egy típus megfelel-e egy adott megszorításnak. Ez hasonló a JavaScript ternáris operátorához, de típusokra alkalmazva.

Alapvető szintaxis

Egy Conditional típus szintaxisa a következő:

T extends U ? X : Y

Gyakorlati példák

Annak meghatározása, hogy egy típus string-e

Hozzuk létre egy típust, amely string-et ad vissza, ha a bemeneti típus string, egyébként pedig number-t:

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

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

Típus kinyerése unióból

Feltételes típusokat használhat egy adott típus kinyerésére egy unió típusból. Például a nem-null értékű típusok kinyerésére:

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

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

Itt, ha a T értéke null vagy undefined, a típus never lesz, amelyet a TypeScript unió típus egyszerűsítése kiszűr.

Típusok kikövetkeztetése (Inferring)

A feltételes típusok az infer kulcsszóval típusok kikövetkeztetésére is használhatók. Ez lehetővé teszi egy típus kinyerését egy összetettebb típusstruktúrából.

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

Ebben a példában a ReturnType kinyeri egy függvény visszatérési típusát. Ellenőrzi, hogy a T egy olyan függvény-e, amely bármilyen argumentumot fogad és egy R típust ad vissza. Ha igen, akkor R-t ad vissza; egyébként any-t.

A Mapped és Conditional típusok kombinálása

A Mapped és Conditional típusok valódi ereje a kombinálásukban rejlik. Ez lehetővé teszi rendkívül rugalmas és kifejező típus-transzformációk létrehozását.

Példa: Mélyen írásvédett (Deep Readonly)

Gyakori felhasználási eset egy olyan típus létrehozása, amely egy objektum minden tulajdonságát, beleértve a beágyazott tulajdonságokat is, írásvédetté tesz. Ezt egy rekurzív feltételes típussal lehet elérni.

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

Itt a DeepReadonly rekurzívan alkalmazza a readonly módosítót minden tulajdonságra és azok beágyazott tulajdonságaira. Ha egy tulajdonság objektum, rekurzívan meghívja a DeepReadonly-t azon az objektumon. Ellenkező esetben egyszerűen csak a readonly módosítót alkalmazza a tulajdonságra.

Példa: Tulajdonságok szűrése típus szerint

Tegyük fel, hogy szeretne létrehozni egy típust, amely csak egy adott típusú tulajdonságokat tartalmaz. Ezt a Mapped és Conditional típusok kombinálásával érheti el.

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

Ebben a példában a FilterByType végigiterál a T tulajdonságain, és ellenőrzi, hogy az egyes tulajdonságok típusa kiterjeszti-e az U-t. Ha igen, akkor bevonja a tulajdonságot az eredménytípusba; ellenkező esetben kizárja azt a kulcs never-re való leképezésével. Figyelje meg az „as” használatát a kulcsok újra-leképezéséhez. Ezután az `Omit` és a `keyof StringProperties` segítségével távolítjuk el a string tulajdonságokat az eredeti interfészből.

Haladó felhasználási esetek és minták

Az alapvető példákon túl a Mapped és Conditional típusok haladóbb forgatókönyvekben is használhatók, hogy rendkívül testreszabható és típusbiztos alkalmazásokat hozzunk létre.

Disztributív feltételes típusok

A feltételes típusok disztributívak, ha az ellenőrzött típus egy unió típus. Ez azt jelenti, hogy a feltétel az unió minden tagjára külön-külön alkalmazódik, és az eredményeket egy új unió típusban egyesítik.

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

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

Ebben a példában a ToArray a string | number unió minden tagjára külön-külön alkalmazódik, ami a string[] | number[] eredményt adja. Ha a feltétel nem lenne disztributív, az eredmény (string | number)[] lett volna.

Segédtípusok (Utility Types) használata

A TypeScript számos beépített segédtípust (utility type) biztosít, amelyek a Mapped és Conditional típusokra épülnek. Ezek a segédtípusok építőelemként használhatók összetettebb típus-transzformációkhoz.

Ezek a segédtípusok hatékony eszközök, amelyek leegyszerűsíthetik az összetett típusmanipulációkat. Például a Pick és a Partial kombinálásával létrehozhat egy típust, amely csak bizonyos tulajdonságokat tesz opcionálissá:

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

Ebben a példában az OptionalDescriptionProduct a Product minden tulajdonságával rendelkezik, de a description tulajdonság opcionális.

Template Literal típusok használata

A Template Literal típusok lehetővé teszik string literálok alapján történő típusok létrehozását. Mapped és Conditional típusokkal kombinálva dinamikus és kifejező típus-transzformációkat hozhatunk létre velük. Például létrehozhat egy típust, amely minden tulajdonságnevet egy adott stringgel lát el előtagként:

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

Ebben a példában a PrefixedSettings-nek data_apiUrl és data_timeout tulajdonságai lesznek.

Jó gyakorlatok és megfontolások

Összegzés

A Mapped Types és a Conditional Types a TypeScript hatékony funkciói, amelyek lehetővé teszik rendkívül rugalmas és kifejező típus-transzformációk létrehozását. Ezen koncepciók elsajátításával javíthatja TypeScript alkalmazásai típusbiztonságát, karbantarthatóságát és általános minőségét. Az egyszerű átalakításoktól, mint a tulajdonságok opcionálissá vagy írásvédetté tétele, a bonyolult rekurzív transzformációkig és feltételes logikáig, ezek a funkciók biztosítják azokat az eszközöket, amelyekre a robusztus és skálázható alkalmazások építéséhez szüksége van. Folytassa a felfedezést és a kísérletezést ezekkel a funkciókkal, hogy kiaknázza teljes potenciáljukat, és még képzettebb TypeScript fejlesztővé váljon.

Ahogy folytatja TypeScript-es utazását, ne felejtse el kihasználni a rendelkezésre álló rengeteg erőforrást, beleértve a hivatalos TypeScript dokumentációt, az online közösségeket és a nyílt forráskódú projekteket. Fogadja el a Mapped és Conditional típusok erejét, és jól felkészült lesz arra, hogy megbirkózzon még a legnehezebb típusokkal kapcsolatos problémákkal is.

A TypeScript Mapped és Conditional típusainak mesteri szintű használata | MLOG