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;
};
T
: Įvesties tipas, kurį norite atvaizduoti.K in keyof T
: Iteruoja per kiekvieną įvesties tipoT
raktą.keyof T
sukuria visų savybių pavadinimų tipoT
junginį (union), oK
iteracijos metu atstovauja kiekvieną atskirą raktą.Transformation
: Transformacija, kurią norite pritaikyti kiekvienai savybei. Tai gali būti modifikatoriaus (pvz.,readonly
ar?
) pridėjimas, tipo pakeitimas ar kažkas visiškai kito.
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
T
: Tikrinamas tipas.U
: Tipas, kurįT
išplečia (sąlyga).X
: Tipas, kuris bus grąžintas, jeiT
išplečiaU
(sąlyga yra teisinga).Y
: Tipas, kuris bus grąžintas, jeiT
neišplečiaU
(sąlyga yra klaidinga).
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.
Partial<T>
: VisasT
savybes paverčia pasirenkamomis.Required<T>
: VisasT
savybes paverčia privalomomis.Readonly<T>
: VisasT
savybes paverčia tik skaitomomis.Pick<T, K>
: IšT
parenka savybių rinkinįK
.Omit<T, K>
: IšT
pašalina savybių rinkinįK
.Record<K, T>
: Sukuria tipą su savybių rinkiniuK
, kurio tipas yraT
.Exclude<T, U>
: IšT
pašalina visus tipus, kuriuos galima priskirtiU
.Extract<T, U>
: IšT
išrenka visus tipus, kuriuos galima priskirtiU
.NonNullable<T>
: IšT
pašalinanull
irundefined
.Parameters<T>
: Gauna funkcijos tipoT
parametrų tipus.ReturnType<T>
: Gauna funkcijos tipoT
grąžinimo tipą.InstanceType<T>
: Gauna konstruktoriaus funkcijos tipoT
egzemplioriaus tipą.
Š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
- Paprastumas: Nors atvaizduotieji ir sąlyginiai tipai yra galingi, jie taip pat gali padaryti jūsų kodą sudėtingesnį. Stenkitės, kad jūsų tipų transformacijos būtų kuo paprastesnės.
- Naudokite pagalbinius tipus: Kai tik įmanoma, pasinaudokite integruotais TypeScript pagalbiniais tipais. Jie yra gerai išbandyti ir gali supaprastinti jūsų kodą.
- Dokumentuokite savo tipus: Aiškiai dokumentuokite savo tipų transformacijas, ypač jei jos yra sudėtingos. Tai padės kitiems programuotojams suprasti jūsų kodą.
- Testuokite savo tipus: Naudokite TypeScript tipų tikrinimą, kad įsitikintumėte, jog jūsų tipų transformacijos veikia taip, kaip tikėtasi. Galite rašyti vienetinius testus (unit tests), kad patikrintumėte savo tipų elgseną.
- Atsižvelkite į našumą: Sudėtingos tipų transformacijos gali paveikti jūsų TypeScript kompiliatoriaus našumą. Būkite atidūs savo tipų sudėtingumui ir venkite nereikalingų skaičiavimų.
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.