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;
};
T
: Sisendtüüp, mida soovite kaardistada.K in keyof T
: Itereerib üle iga võtme sisendtüübisT
.keyof T
loob kõigiT
omaduste nimede ühendi (union) jaK
esindab iteratsiooni ajal iga individuaalset võtit.Transformation
: Transformatsioon, mida soovite igale omadusele rakendada. See võib olla modifikaatori (nagureadonly
või?
) lisamine, tüübi muutmine või midagi muud.
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
T
: Kontrollitav tüüp.U
: Tüüp, midaT
laiendab (tingimus).X
: Tüüp, mis tagastatakse, kuiT
laiendab tüüpiU
(tingimus on tõene).Y
: Tüüp, mis tagastatakse, kuiT
ei laienda tüüpiU
(tingimus on väär).
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.
Partial<T>
: Muudab kõikT
omadused valikuliseks.Required<T>
: Muudab kõikT
omadused kohustuslikuks.Readonly<T>
: Muudab kõikT
omadused kirjutuskaitstuks.Pick<T, K>
: Valib tüübistT
omaduste hulgaK
.Omit<T, K>
: Eemaldab tüübistT
omaduste hulgaK
.Record<K, T>
: Konstrueerib tüübi omaduste hulgagaK
, mis on tüübigaT
.Exclude<T, U>
: Jätab tüübistT
välja kõik tüübid, mis on omistatavad tüübileU
.Extract<T, U>
: Eraldab tüübistT
kõik tüübid, mis on omistatavad tüübileU
.NonNullable<T>
: Jätab tüübistT
väljanull
jaundefined
.Parameters<T>
: Hangib funktsioonitüübiT
parameetrid.ReturnType<T>
: Hangib funktsioonitüübiT
tagastustüübi.InstanceType<T>
: Hangib konstruktorfunktsiooni tüübiT
instantsitüübi.
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
- Hoidke see lihtsana: Kuigi kaardistatud tüübid ja tingimuslikud tüübid on võimsad, võivad nad muuta teie koodi ka keerukamaks. Püüdke hoida oma tüübitransformatsioonid nii lihtsad kui võimalik.
- Kasutage abistavaid tüüpe: Kasutage võimaluse korral TypeScripti sisseehitatud abistavaid tüüpe. Need on hästi testitud ja võivad teie koodi lihtsustada.
- Dokumenteerige oma tüübid: Dokumenteerige oma tüübitransformatsioonid selgelt, eriti kui need on keerulised. See aitab teistel arendajatel teie koodi mõista.
- Testige oma tüüpe: Kasutage TypeScripti tüübikontrolli, et veenduda oma tüübitransformatsioonide ootuspärases toimimises. Saate kirjutada ühikteste oma tüüpide käitumise kontrollimiseks.
- Arvestage jõudlusega: Keerulised tüübitransformatsioonid võivad mõjutada teie TypeScripti kompilaatori jõudlust. Olge teadlik oma tüüpide keerukusest ja vältige tarbetuid arvutusi.
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.