Lietuvių

Išsamus vadovas apie TypeScript atvaizduotuosius ir sąlyginius tipus su praktiniais pavyzdžiais, kaip kurti patikimas ir tipų saugumo požiūriu tvirtas programas.

TypeScript atvaizduotųjų ir sąlyginių tipų įsisavinimas

TypeScript, JavaScript viršaibis, siūlo galingas funkcijas, skirtas kurti patikimas ir lengvai prižiūrimas programas. Tarp šių funkcijų, atvaizduotieji tipai (Mapped Types) ir sąlyginiai tipai (Conditional Types) išsiskiria kaip esminiai įrankiai pažangiam tipų manipuliavimui. Šis vadovas pateikia išsamią šių koncepcijų apžvalgą, nagrinėjant jų sintaksę, praktinius pritaikymus ir pažangius panaudojimo atvejus. Nesvarbu, ar esate patyręs TypeScript programuotojas, ar tik pradedate savo kelionę, šis straipsnis suteiks jums žinių, kaip efektyviai išnaudoti šias funkcijas.

Kas yra atvaizduotieji tipai?

Atvaizduotieji tipai leidžia kurti naujus tipus transformuojant esamus. Jie iteruoja per esamo tipo savybes ir kiekvienai savybei pritaiko transformaciją. Tai ypač naudinga kuriant esamų tipų variantus, pavyzdžiui, paverčiant visas savybes pasirenkamomis (optional) arba tik skaitomomis (read-only).

Pagrindinė sintaksė

Atvaizduotojo tipo sintaksė yra tokia:

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

Praktiniai pavyzdžiai

Savybių pavertimas tik skaitomomis

Tarkime, turite sąsają, aprašančią vartotojo profilį:

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

Galite sukurti naują tipą, kuriame visos savybės yra tik skaitomos:

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

Dabar ReadOnlyUserProfile turės tas pačias savybes kaip ir UserProfile, bet visos jos bus tik skaitomos.

Savybių pavertimas pasirenkamomis

Panašiai galite visas savybes paversti pasirenkamomis:

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

OptionalUserProfile turės visas UserProfile savybes, tačiau kiekviena iš jų bus pasirenkama.

Savybių tipų modifikavimas

Taip pat galite modifikuoti kiekvienos savybės tipą. Pavyzdžiui, galite visas savybes paversti eilutėmis (string):

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

Šiuo atveju visos StringifiedUserProfile savybės bus string tipo.

Kas yra sąlyginiai tipai?

Sąlyginiai tipai leidžia apibrėžti tipus, kurie priklauso nuo tam tikros sąlygos. Jie suteikia būdą išreikšti tipų ryšius, priklausomai nuo to, ar tipas atitinka konkretų apribojimą. Tai panašu į JavaScript trejybį operatorių (ternary operator), tik skirta tipams.

Pagrindinė sintaksė

Sąlyginio tipo sintaksė yra tokia:

T extends U ? X : Y

Praktiniai pavyzdžiai

Nustatymas, ar tipas yra eilutė (string)

Sukurkime tipą, kuris grąžina string, jei įvesties tipas yra eilutė, ir number kitu atveju:

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

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

Tipo išskyrimas iš junginio (union)

Galite naudoti sąlyginius tipus, norėdami išskirti konkretų tipą iš junginio tipo. Pavyzdžiui, norėdami išskirti tipus, kurie nėra `null`:

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

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

Čia, jei T yra null arba undefined, tipas tampa never, kurį vėliau TypeScript pašalina supaprastindamas junginio tipą.

Tipų išvedimas (Inferring)

Sąlyginiai tipai taip pat gali būti naudojami tipams išvesti naudojant raktažodį infer. Tai leidžia išgauti tipą iš sudėtingesnės tipo struktūros.

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

Šiame pavyzdyje ReturnType ištraukia funkcijos grąžinimo tipą. Jis patikrina, ar T yra funkcija, priimanti bet kokius argumentus ir grąžinanti tipą R. Jei taip, jis grąžina R; kitu atveju, grąžina any.

Atvaizduotųjų ir sąlyginių tipų derinimas

Tikroji atvaizduotųjų ir sąlyginių tipų galia atsiskleidžia juos derinant. Tai leidžia kurti itin lanksčias ir išraiškingas tipų transformacijas.

Pavyzdys: Gilus „Readonly“

Dažnas panaudojimo atvejis – sukurti tipą, kuris visas objekto savybes, įskaitant ir įdėtąsias, paverčia tik skaitomomis. Tai galima pasiekti naudojant rekursinį sąlyginį tipą.

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

Čia DeepReadonly rekursyviai pritaiko readonly modifikatorių visoms savybėms ir jų įdėtosioms savybėms. Jei savybė yra objektas, jis rekursyviai iškviečia DeepReadonly tam objektui. Kitu atveju jis tiesiog pritaiko readonly modifikatorių savybei.

Pavyzdys: Savybių filtravimas pagal tipą

Tarkime, norite sukurti tipą, kuris apimtų tik tam tikro tipo savybes. Tai galite pasiekti derindami atvaizduotuosius ir sąlyginius tipus.

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

Šiame pavyzdyje FilterByType iteruoja per T savybes ir tikrina, ar kiekvienos savybės tipas išplečia U. Jei taip, savybė įtraukiama į gautą tipą; kitu atveju ji pašalinama, priskiriant raktui never tipą. Atkreipkite dėmesį į „as“ naudojimą raktų pervaizdavimui. Tada mes naudojame `Omit` ir `keyof StringProperties` tam, kad pašalintume eilutės tipo savybes iš pradinės sąsajos.

Pažangūs panaudojimo atvejai ir šablonai

Be pagrindinių pavyzdžių, atvaizduotieji ir sąlyginiai tipai gali būti naudojami sudėtingesniuose scenarijuose, siekiant sukurti itin pritaikomas ir tipų saugumo požiūriu tvirtas programas.

Pasiskirstantys (distributive) sąlyginiai tipai

Sąlyginiai tipai yra pasiskirstantys (distributive), kai tikrinamas tipas yra junginys (union). Tai reiškia, kad sąlyga taikoma kiekvienam junginio nariui atskirai, o rezultatai sujungiami į naują junginio tipą.

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

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

Šiame pavyzdyje ToArray yra pritaikomas kiekvienam junginio string | number nariui atskirai, todėl gauname string[] | number[]. Jei sąlyga nebūtų pasiskirstanti, rezultatas būtų (string | number)[].

Pagalbinių tipų (Utility Types) naudojimas

TypeScript pateikia kelis integruotus pagalbinius tipus, kurie išnaudoja atvaizduotuosius ir sąlyginius tipus. Šie pagalbiniai tipai gali būti naudojami kaip statybiniai blokai sudėtingesnėms tipų transformacijoms.

Šie pagalbiniai tipai yra galingi įrankiai, galintys supaprastinti sudėtingas tipų manipuliacijas. Pavyzdžiui, galite sujungti Pick ir Partial, kad sukurtumėte tipą, kuris tik tam tikras savybes paverčia pasirenkamomis:

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

Šiame pavyzdyje OptionalDescriptionProduct turi visas Product savybes, tačiau description savybė yra pasirenkama.

Šabloninių literalių tipų (Template Literal Types) naudojimas

Šabloniniai literalių tipai leidžia kurti tipus, pagrįstus eilučių literalais. Jie gali būti naudojami kartu su atvaizduotaisiais ir sąlyginiais tipais kuriant dinamiškas ir išraiškingas tipų transformacijas. Pavyzdžiui, galite sukurti tipą, kuris visų savybių pavadinimus papildo konkrečia priešdėline eilute:

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

Šiame pavyzdyje PrefixedSettings turės savybes data_apiUrl ir data_timeout.

Geriausios praktikos ir svarstymai

Išvada

Atvaizduotieji tipai ir sąlyginiai tipai yra galingos TypeScript funkcijos, leidžiančios kurti itin lanksčias ir išraiškingas tipų transformacijas. Įsisavinę šias koncepcijas, galite pagerinti savo TypeScript programų tipų saugumą, palaikomumą ir bendrą kokybę. Nuo paprastų transformacijų, tokių kaip savybių pavertimas pasirenkamomis ar tik skaitomomis, iki sudėtingų rekursinių transformacijų ir sąlyginės logikos – šios funkcijos suteikia įrankius, kurių reikia norint kurti patikimas ir mastelį atitinkančias programas. Toliau tyrinėkite ir eksperimentuokite su šiomis funkcijomis, kad atskleistumėte visą jų potencialą ir taptumėte labiau patyrusiu TypeScript programuotoju.

Tęsdami savo TypeScript kelionę, nepamirškite pasinaudoti gausiais prieinamais ištekliais, įskaitant oficialią TypeScript dokumentaciją, internetines bendruomenes ir atvirojo kodo projektus. Priimkite atvaizduotųjų ir sąlyginių tipų galią ir būsite gerai pasirengę įveikti net sudėtingiausias su tipais susijusias problemas.