Suomi

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

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

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.

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

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.