Eesti

Põhjalik juhend TypeScript'i võimsate kaardistatud ja tingimuslike tüüpide kohta, sisaldades praktilisi näiteid ja keerukamaid kasutusjuhte robustsete ja tüübikindlate rakenduste loomiseks.

TypeScript'i kaardistatud tüüpide ja tingimuslike tüüpide valdamine

TypeScript, JavaScripti ülemhulk, pakub võimsaid funktsioone robustsete ja hooldatavate rakenduste loomiseks. Nende funktsioonide seas paistavad kaardistatud tüübid (Mapped Types) ja tingimuslikud tüübid (Conditional Types) silma kui olulised tööriistad edasijõudnud tüübimanipulatsiooniks. See juhend annab põhjaliku ülevaate nendest kontseptsioonidest, uurides nende süntaksit, praktilisi rakendusi ja keerukamaid kasutusjuhte. Olenemata sellest, kas olete kogenud TypeScripti arendaja või alles alustate oma teekonda, varustab see artikkel teid teadmistega, et neid funktsioone tõhusalt kasutada.

Mis on kaardistatud tüübid?

Kaardistatud tüübid võimaldavad teil luua uusi tüüpe, muutes olemasolevaid. Nad itereerivad üle olemasoleva tüübi omaduste ja rakendavad igale omadusele transformatsiooni. See on eriti kasulik olemasolevate tüüpide variatsioonide loomiseks, näiteks muutes kõik omadused valikuliseks või kirjutuskaitstuks.

Põhisüntaks

Kaardistatud tüübi süntaks on järgmine:

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

Praktilised näited

Omaduste muutmine kirjutuskaitstuks

Oletame, et teil on liides, mis esindab kasutajaprofiili:

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

Saate luua uue tüübi, kus kõik omadused on kirjutuskaitstud:

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

Nüüd on tüübil ReadOnlyUserProfile samad omadused mis tüübil UserProfile, kuid need on kõik kirjutuskaitstud.

Omaduste muutmine valikuliseks

Sarnaselt saate muuta kõik omadused valikuliseks:

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

Tüübil OptionalUserProfile on kõik UserProfile'i omadused, kuid iga omadus on valikuline.

Omaduste tüüpide muutmine

Saate muuta ka iga omaduse tüüpi. Näiteks saate muuta kõik omadused sõnedeks (string):

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

Sel juhul on kõik omadused tüübis StringifiedUserProfile tüübiga string.

Mis on tingimuslikud tüübid?

Tingimuslikud tüübid võimaldavad teil defineerida tüüpe, mis sõltuvad tingimusest. Need pakuvad viisi tüübisuhete väljendamiseks selle põhjal, kas tüüp vastab teatud piirangule. See sarnaneb JavaScripti kolmekomponendilise operaatoriga (ternary operator), kuid tüüpide jaoks.

Põhisüntaks

Tingimusliku tüübi süntaks on järgmine:

T extends U ? X : Y

Praktilised näited

Tüübi määramine, kas tegemist on sõnega

Loome tüübi, mis tagastab string, kui sisendtüüp on sõne, ja muul juhul number:

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

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

Tüübi eraldamine ühendist (Union)

Saate kasutada tingimuslikke tüüpe, et eraldada ühendtüübist (union type) konkreetne tüüp. Näiteks tühiväärtuseta (non-nullable) tüüpide eraldamiseks:

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

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

Siin, kui T on null või undefined, muutub tüübiks never, mille TypeScripti ühendtüübi lihtsustamine seejärel välja filtreerib.

Tüüpide järeldamine (Inferring)

Tingimuslikke tüüpe saab kasutada ka tüüpide järeldamiseks, kasutades võtmesõna infer. See võimaldab teil eraldada tüübi keerukamast tüübistruktuurist.

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

Selles näites eraldab ReturnType funktsiooni tagastustüübi. See kontrollib, kas T on funktsioon, mis võtab vastu mis tahes argumente ja tagastab tüübi R. Kui jah, siis tagastab see R; vastasel juhul tagastab see any.

Kaardistatud tüüpide ja tingimuslike tüüpide kombineerimine

Kaardistatud tüüpide ja tingimuslike tüüpide tegelik võimsus tuleb nende kombineerimisest. See võimaldab teil luua väga paindlikke ja väljendusrikkaid tüübitransformatsioone.

Näide: Sügav kirjutuskaitse (Deep Readonly)

Levinud kasutusjuht on luua tüüp, mis muudab kõik objekti omadused, sealhulgas pesastatud omadused, kirjutuskaitstuks. Seda saab saavutada rekursiivse tingimusliku tüübi abil.

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

Siin rakendab DeepReadonly rekursiivselt readonly modifikaatorit kõikidele omadustele ja nende pesastatud omadustele. Kui omadus on objekt, kutsub see rekursiivselt välja DeepReadonly selle objekti peal. Vastasel juhul rakendab see lihtsalt omadusele readonly modifikaatori.

Näide: Omaduste filtreerimine tüübi järgi

Oletame, et soovite luua tüübi, mis sisaldab ainult teatud tüüpi omadusi. Selle saavutamiseks saate kombineerida kaardistatud tüüpe ja tingimuslikke tüüpe.

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

Selles näites itereerib FilterByType üle T omaduste ja kontrollib, kas iga omaduse tüüp laiendab tüüpi U. Kui jah, siis lisab see omaduse tulemuseks olevasse tüüpi; vastasel juhul jätab selle välja, kaardistades võtme tüübile never. Pange tähele "as" kasutamist võtmete ümberkaardistamiseks. Seejärel kasutame `Omit` ja `keyof StringProperties`, et eemaldada sõne-tüüpi omadused algsest liidesest.

Edasijõudnud kasutusjuhud ja mustrid

Lisaks põhinäidetele saab kaardistatud tüüpe ja tingimuslikke tüüpe kasutada keerukamates stsenaariumides, et luua väga kohandatavaid ja tüübikindlaid rakendusi.

Distributiivsed tingimuslikud tüübid

Tingimuslikud tüübid on distributiivsed, kui kontrollitav tüüp on ühendtüüp (union type). See tähendab, et tingimust rakendatakse igale ühendi liikmele eraldi ja tulemused kombineeritakse seejärel uueks ühendtüübiks.

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

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

Selles näites rakendatakse ToArray igale ühendi string | number liikmele eraldi, mille tulemuseks on string[] | number[]. Kui tingimus ei oleks distributiivne, oleks tulemus olnud (string | number)[].

Abistavate tüüpide (Utility Types) kasutamine

TypeScript pakub mitmeid sisseehitatud abistavaid tüüpe, mis kasutavad kaardistatud tüüpe ja tingimuslikke tüüpe. Neid abistavaid tüüpe saab kasutada keerukamate tüübitransformatsioonide ehitusplokkidena.

Need abistavad tüübid on võimsad tööriistad, mis võivad keerukaid tüübimanipulatsioone lihtsustada. Näiteks saate kombineerida tüüpe Pick ja Partial, et luua tüüp, mis muudab ainult teatud omadused valikuliseks:

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

Selles näites on tüübil OptionalDescriptionProduct kõik Product'i omadused, kuid omadus description on valikuline.

Mall-literaaltüüpide (Template Literal Types) kasutamine

Mall-literaaltüübid võimaldavad teil luua tüüpe, mis põhinevad sõneliteraalidel. Neid saab kombineerida kaardistatud tüüpide ja tingimuslike tüüpidega, et luua dünaamilisi ja väljendusrikkaid tüübitransformatsioone. Näiteks saate luua tüübi, mis lisab kõigile omaduste nimedele eesliite:

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

Selles näites on tüübil PrefixedSettings omadused data_apiUrl ja data_timeout.

Parimad praktikad ja kaalutlused

Kokkuvõte

Kaardistatud tüübid ja tingimuslikud tüübid on TypeScripti võimsad funktsioonid, mis võimaldavad teil luua väga paindlikke ja väljendusrikkaid tüübitransformatsioone. Nende kontseptsioonide valdamisega saate parandada oma TypeScripti rakenduste tüübikindlust, hooldatavust ja üldist kvaliteeti. Alates lihtsatest transformatsioonidest, nagu omaduste valikuliseks või kirjutuskaitstuks muutmine, kuni keerukate rekursiivsete transformatsioonide ja tingimusloogikani – need funktsioonid pakuvad teile tööriistu, mida vajate robustsete ja skaleeritavate rakenduste ehitamiseks. Jätkake nende funktsioonide uurimist ja nendega katsetamist, et avada nende täielik potentsiaal ja saada osavamaks TypeScripti arendajaks.

Oma TypeScripti teekonda jätkates ärge unustage kasutada rikkalikke saadaolevaid ressursse, sealhulgas ametlikku TypeScripti dokumentatsiooni, veebikogukondi ja avatud lähtekoodiga projekte. Võtke omaks kaardistatud tüüpide ja tingimuslike tüüpide võimsus ning olete hästi varustatud, et lahendada ka kõige keerulisemaid tüüpidega seotud probleeme.