Kattava opas TypeScriptin tehokkaisiin Mapped-tyyppeihin ja ehtotyyppeihin, sisältäen käytännön esimerkkejä ja edistyneitä käyttötapauksia vankkojen ja tyyppiturvallisten sovellusten luomiseksi.
TypeScriptin Mapped-tyyppien ja ehtotyyppien hallinta
TypeScript, JavaScriptin supersarja, tarjoaa tehokkaita ominaisuuksia vankkojen ja ylläpidettävien sovellusten luomiseen. Näiden ominaisuuksien joukossa Mapped-tyypit ja ehtotyypit erottuvat olennaisina työkaluina edistyneeseen tyyppien manipulointiin. Tämä opas tarjoaa kattavan yleiskatsauksen näistä käsitteistä, tutkien niiden syntaksia, käytännön sovelluksia ja edistyneitä käyttötapauksia. Olitpa kokenut TypeScript-kehittäjä tai vasta aloittamassa matkaasi, tämä artikkeli antaa sinulle tiedot näiden ominaisuuksien tehokkaaseen hyödyntämiseen.
Mitä ovat Mapped-tyypit?
Mapped-tyyppien avulla voit luoda uusia tyyppejä muuntamalla olemassa olevia. Ne iteroivat olemassa olevan tyypin ominaisuuksien yli ja soveltavat muunnosta jokaiseen ominaisuuteen. Tämä on erityisen hyödyllistä luotaessa variaatioita olemassa olevista tyypeistä, kuten tekemällä kaikista ominaisuuksista valinnaisia tai vain luku -muotoisia.
Perussyntaksi
Mapped-tyypin syntaksi on seuraava:
type NewType<T> = {
[K in keyof T]: Transformation;
};
T
: Syötetyyppi, jonka yli haluat mapata.K in keyof T
: Iteroi jokaisen avaimen yli syötetyypissäT
.keyof T
luo unionin kaikista ominaisuuksien nimistä tyypissäT
, jaK
edustaa kutakin yksittäistä avainta iteraation aikana.Transformation
: Muunnos, jonka haluat soveltaa jokaiseen ominaisuuteen. Tämä voi olla modifioijan lisääminen (kutenreadonly
tai?
), tyypin muuttaminen tai jotain aivan muuta.
Käytännön esimerkkejä
Ominaisuuksien tekeminen vain luku -muotoisiksi
Oletetaan, että sinulla on käyttöliittymä, joka edustaa käyttäjäprofiilia:
interface UserProfile {
name: string;
age: number;
email: string;
}
Voit luoda uuden tyypin, jossa kaikki ominaisuudet ovat vain luku -muotoisia:
type ReadOnlyUserProfile = {
readonly [K in keyof UserProfile]: UserProfile[K];
};
Nyt ReadOnlyUserProfile
-tyypillä on samat ominaisuudet kuin UserProfile
-tyypillä, mutta ne kaikki ovat vain luku -muotoisia.
Ominaisuuksien tekeminen valinnaisiksi
Vastaavasti voit tehdä kaikista ominaisuuksista valinnaisia:
type OptionalUserProfile = {
[K in keyof UserProfile]?: UserProfile[K];
};
OptionalUserProfile
-tyypillä on kaikki UserProfile
-tyypin ominaisuudet, mutta jokainen ominaisuus on valinnainen.
Ominaisuuksien tyyppien muokkaaminen
Voit myös muokata kunkin ominaisuuden tyyppiä. Voit esimerkiksi muuntaa kaikki ominaisuudet merkkijonoiksi:
type StringifiedUserProfile = {
[K in keyof UserProfile]: string;
};
Tässä tapauksessa kaikki StringifiedUserProfile
-tyypin ominaisuudet ovat tyyppiä string
.
Mitä ovat ehtotyypit?
Ehtotyypit mahdollistavat tyyppien määrittelyn, jotka riippuvat ehdosta. Ne tarjoavat tavan ilmaista tyyppisuhteita sen perusteella, täyttääkö tyyppi tietyn rajoituksen. Tämä on samanlainen kuin ternary-operaattori JavaScriptissä, mutta tyypeille.
Perussyntaksi
Ehtotyypin syntaksi on seuraava:
T extends U ? X : Y
T
: Tarkasteltava tyyppi.U
: Tyyppi, jotaT
laajentaa (ehto).X
: Tyyppi, joka palautetaan, josT
laajentaa tyyppiäU
(ehto on tosi).Y
: Tyyppi, joka palautetaan, josT
ei laajenna tyyppiäU
(ehto on epätosi).
Käytännön esimerkkejä
Sen määrittäminen, onko tyyppi merkkijono
Luodaan tyyppi, joka palauttaa string
, jos syötetyyppi on merkkijono, ja muuten number
:
type StringOrNumber<T> = T extends string ? string : number;
type Result1 = StringOrNumber<string>; // string
type Result2 = StringOrNumber<number>; // number
type Result3 = StringOrNumber<boolean>; // number
Tyypin poimiminen unioniosta
Voit käyttää ehtotyyppejä tietyn tyypin poimimiseen unioniotyypistä. Esimerkiksi ei-null-arvoisten tyyppien poimiminen:
type NonNullable<T> = T extends null | undefined ? never : T;
type Result4 = NonNullable<string | null | undefined>; // string
Tässä, jos T
on null
tai undefined
, tyypiksi tulee never
, jonka TypeScriptin unioniotyyppien yksinkertaistaminen suodattaa pois.
Tyyppien päättely
Ehtotyyppejä voidaan myös käyttää tyyppien päättelemiseen infer
-avainsanan avulla. Tämä mahdollistaa tyypin poimimisen monimutkaisemmasta tyyppirakenteesta.
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
Tässä esimerkissä ReturnType
poimii funktion paluutyypin. Se tarkistaa, onko T
funktio, joka hyväksyy mitä tahansa argumentteja ja palauttaa tyypin R
. Jos on, se palauttaa R
; muuten se palauttaa any
.
Mapped-tyyppien ja ehtotyyppien yhdistäminen
Mapped-tyyppien ja ehtotyyppien todellinen voima tulee niiden yhdistämisestä. Tämä mahdollistaa erittäin joustavien ja ilmaisuvoimaisten tyyppimuunnosten luomisen.
Esimerkki: Syvä vain luku -muoto (Deep Readonly)
Yleinen käyttötapaus on luoda tyyppi, joka tekee kaikista objektin ominaisuuksista, mukaan lukien sisäkkäisistä ominaisuuksista, vain luku -muotoisia. Tämä voidaan saavuttaa käyttämällä rekursiivista ehtotyyppiä.
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>;
Tässä DeepReadonly
soveltaa rekursiivisesti readonly
-modifioijaa kaikkiin ominaisuuksiin ja niiden sisäkkäisiin ominaisuuksiin. Jos ominaisuus on objekti, se kutsuu rekursiivisesti DeepReadonly
-tyyppiä sille objektille. Muussa tapauksessa se vain soveltaa readonly
-modifioijaa ominaisuuteen.
Esimerkki: Ominaisuuksien suodattaminen tyypin mukaan
Oletetaan, että haluat luoda tyypin, joka sisältää vain tietyn tyyppisiä ominaisuuksia. Voit yhdistää Mapped-tyyppejä ja ehtotyyppejä saavuttaaksesi tämän.
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>;
Tässä esimerkissä FilterByType
iteroi T
:n ominaisuuksien yli ja tarkistaa, laajentaako kunkin ominaisuuden tyyppi tyyppiä U
. Jos näin on, se sisällyttää ominaisuuden tuloksena olevaan tyyppiin; muussa tapauksessa se jättää sen pois mapittamalla avaimen never
-tyyppiin. Huomaa "as"-avainsanan käyttö avainten uudelleenmapittamiseen. Tämän jälkeen käytämme `Omit`-tyyppiä ja `keyof StringProperties` -rakennetta poistaaksemme merkkijono-ominaisuudet alkuperäisestä käyttöliittymästä.
Edistyneet käyttötapaukset ja mallit
Perusesimerkkien lisäksi Mapped-tyyppejä ja ehtotyyppejä voidaan käyttää edistyneemmissä skenaarioissa erittäin muokattavien ja tyyppiturvallisten sovellusten luomiseksi.
Distributiiviset ehtotyypit
Ehtotyypit ovat distributiivisia, kun tarkasteltava tyyppi on unioniotyyppi. Tämä tarkoittaa, että ehto sovelletaan jokaiseen unionin jäseneen erikseen, ja tulokset yhdistetään sitten uudeksi unioniotyypiksi.
type ToArray<T> = T extends any ? T[] : never;
type Result6 = ToArray<string | number>; // string[] | number[]
Tässä esimerkissä ToArray
sovelletaan jokaiseen unionin string | number
jäseneen erikseen, mikä johtaa tulokseen string[] | number[]
. Jos ehto ei olisi distributiivinen, tulos olisi ollut (string | number)[]
.
Aputyyppien käyttö
TypeScript tarjoaa useita sisäänrakennettuja aputyyppejä, jotka hyödyntävät Mapped-tyyppejä ja ehtotyyppejä. Näitä aputyyppejä voidaan käyttää rakennuspalikoina monimutkaisemmille tyyppimuunnoksille.
Partial<T>
: Tekee kaikistaT
:n ominaisuuksista valinnaisia.Required<T>
: Tekee kaikistaT
:n ominaisuuksista pakollisia.Readonly<T>
: Tekee kaikistaT
:n ominaisuuksista vain luku -muotoisia.Pick<T, K>
: Valitsee joukon ominaisuuksiaK
tyypistäT
.Omit<T, K>
: Poistaa joukon ominaisuuksiaK
tyypistäT
.Record<K, T>
: Rakentaa tyypin, jolla on joukko ominaisuuksiaK
tyyppiäT
.Exclude<T, U>
: Poistaa tyypistäT
kaikki tyypit, jotka ovat sijoitettavissa tyyppiinU
.Extract<T, U>
: Poimii tyypistäT
kaikki tyypit, jotka ovat sijoitettavissa tyyppiinU
.NonNullable<T>
: Poistaanull
- jaundefined
-arvot tyypistäT
.Parameters<T>
: Saa funktiomuotoisen tyypinT
parametrit.ReturnType<T>
: Saa funktiomuotoisen tyypinT
paluutyypin.InstanceType<T>
: Saa konstruktorifunktiomuotoisen tyypinT
instanssityypin.
Nämä aputyypit ovat tehokkaita työkaluja, jotka voivat yksinkertaistaa monimutkaisia tyyppimanipulaatioita. Voit esimerkiksi yhdistää Pick
- ja Partial
-tyyppejä luodaksesi tyypin, joka tekee vain tietyistä ominaisuuksista valinnaisia:
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">;
Tässä esimerkissä OptionalDescriptionProduct
-tyypillä on kaikki Product
-tyypin ominaisuudet, mutta description
-ominaisuus on valinnainen.
Mallinekirjaintyyppien käyttö
Mallinekirjaintyypit (Template Literal Types) mahdollistavat tyyppien luomisen merkkijonoliteraalien perusteella. Niitä voidaan käyttää yhdessä Mapped-tyyppien ja ehtotyyppien kanssa dynaamisten ja ilmaisuvoimaisten tyyppimuunnosten luomiseksi. Voit esimerkiksi luoda tyypin, joka lisää kaikkiin ominaisuuksien nimiin tietyn etuliitteen:
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_">;
Tässä esimerkissä PrefixedSettings
-tyypillä on ominaisuudet data_apiUrl
ja data_timeout
.
Parhaat käytännöt ja huomioitavat seikat
- Pidä se yksinkertaisena: Vaikka Mapped-tyypit ja ehtotyypit ovat tehokkaita, ne voivat myös tehdä koodistasi monimutkaisempaa. Yritä pitää tyyppimuunnoksesi mahdollisimman yksinkertaisina.
- Käytä aputyyppejä: Hyödynnä TypeScriptin sisäänrakennettuja aputyyppejä aina kun mahdollista. Ne ovat hyvin testattuja ja voivat yksinkertaistaa koodiasi.
- Dokumentoi tyyppisi: Dokumentoi tyyppimuunnoksesi selkeästi, varsinkin jos ne ovat monimutkaisia. Tämä auttaa muita kehittäjiä ymmärtämään koodiasi.
- Testaa tyyppisi: Käytä TypeScriptin tyyppitarkistusta varmistaaksesi, että tyyppimuunnoksesi toimivat odotetusti. Voit kirjoittaa yksikkötestejä varmistaaksesi tyyppiesi käyttäytymisen.
- Harkitse suorituskykyä: Monimutkaiset tyyppimuunnokset voivat vaikuttaa TypeScript-kääntäjäsi suorituskykyyn. Ole tietoinen tyyppiesi monimutkaisuudesta ja vältä tarpeettomia laskutoimituksia.
Yhteenveto
Mapped-tyypit ja ehtotyypit ovat tehokkaita ominaisuuksia TypeScriptissä, jotka mahdollistavat erittäin joustavien ja ilmaisuvoimaisten tyyppimuunnosten luomisen. Hallitsemalla nämä käsitteet voit parantaa TypeScript-sovellustesi tyyppiturvallisuutta, ylläpidettävyyttä ja yleistä laatua. Yksinkertaisista muunnoksista, kuten ominaisuuksien tekemisestä valinnaisiksi tai vain luku -muotoisiksi, monimutkaisiin rekursiivisiin muunnoksiin ja ehtologiikkaan, nämä ominaisuudet tarjoavat työkalut, joita tarvitset vankkojen ja skaalautuvien sovellusten rakentamiseen. Jatka näiden ominaisuuksien tutkimista ja kokeilemista avataksesi niiden koko potentiaalin ja tullaksesi taitavammaksi TypeScript-kehittäjäksi.
Kun jatkat TypeScript-matkaasi, muista hyödyntää saatavilla olevia resursseja, kuten virallista TypeScript-dokumentaatiota, verkkoyhteisöjä ja avoimen lähdekoodin projekteja. Ota Mapped-tyyppien ja ehtotyyppien voima käyttöön, ja olet hyvin varustautunut selviytymään haastavimmistakin tyyppeihin liittyvistä ongelmista.