Suomi

Tutustu TypeScriptin discriminated unioneihin, tehokkaaseen työkaluun vankkojen ja tyypiturvallisten tilakoneiden rakentamiseen. Opi määrittelemään tiloja, käsittelemään siirtymiä ja hyödyntämään TypeScriptin tyyppijärjestelmää koodin luotettavuuden parantamiseksi.

TypeScriptin Discriminated Unionit: Tyypiturvallisten tilakoneiden rakentaminen

Ohjelmistokehityksen maailmassa sovelluksen tilan tehokas hallinta on ratkaisevan tärkeää. Tilakoneet tarjoavat tehokkaan abstraktion monimutkaisten tilallisten järjestelmien mallintamiseen, varmistaen ennustettavan käyttäytymisen ja yksinkertaistaen järjestelmän logiikan ymmärtämistä. TypeScript, vahvan tyyppijärjestelmänsä ansiosta, tarjoaa fantastisen mekanismin tyypiturvallisten tilakoneiden rakentamiseen käyttämällä discriminated unioneita (tunnetaan myös nimillä tagged unions tai algebralliset tietotyypit).

Mitä ovat Discriminated Unionit?

Discriminated union on tyyppi, joka edustaa arvoa, joka voi olla yksi useista eri tyypeistä. Jokaisella näistä tyypeistä, joita kutsutaan unionin jäseniksi, on yhteinen, erottuva ominaisuus, jota kutsutaan erottelijaksi (discriminant) tai tunnisteeksi (tag). Tämä erottelija antaa TypeScriptin määrittää tarkasti, mikä unionin jäsen on tällä hetkellä aktiivinen, mikä mahdollistaa tehokkaan tyyppitarkistuksen ja automaattisen täydennyksen.

Ajattele sitä liikennevalona. Se voi olla yhdessä kolmesta tilasta: punainen, keltainen tai vihreä. 'Väri'-ominaisuus toimii erottelijana, kertoen meille tarkalleen, missä tilassa valo on.

Miksi käyttää Discriminated Unioneita tilakoneissa?

Discriminated unionit tuovat useita keskeisiä etuja tilakoneita rakennettaessa TypeScriptissä:

Tilakoneen määrittäminen Discriminated Unioneilla

Havainnollistetaan, kuinka tilakone määritellään discriminated unioneilla käytännön esimerkin avulla: tilausten käsittelyjärjestelmä. Tilaus voi olla seuraavissa tiloissa: Odottaa, Käsittelyssä, Lähetetty ja Toimitettu.

Vaihe 1: Määritä tilatyypit

Ensin määrittelemme yksittäiset tyypit kullekin tilalle. Jokaisella tyypillä on `type`-ominaisuus, joka toimii erottelijana, sekä kaikki tilakohtaiset tiedot.


interface Pending {
  type: "pending";
  orderId: string;
  customerName: string;
  items: string[];
}

interface Processing {
  type: "processing";
  orderId: string;
  assignedAgent: string;
}

interface Shipped {
  type: "shipped";
  orderId: string;
  trackingNumber: string;
}

interface Delivered {
  type: "delivered";
  orderId: string;
  deliveryDate: Date;
}

Vaihe 2: Luo Discriminated Union -tyyppi

Seuraavaksi luomme discriminated unionin yhdistämällä nämä yksittäiset tyypit `|` (unioni) -operaattorilla.


type OrderState = Pending | Processing | Shipped | Delivered;

Nyt `OrderState` edustaa arvoa, joka voi olla joko `Pending`, `Processing`, `Shipped` tai `Delivered`. Kunkin tilan sisällä oleva `type`-ominaisuus toimii erottelijana, jonka avulla TypeScript voi erottaa ne toisistaan.

Tilasiirtymien käsittely

Nyt kun olemme määrittäneet tilakoneemme, tarvitsemme mekanismin tilojen välillä siirtymiseen. Luodaan `processOrder`-funktio, joka ottaa syötteenä nykyisen tilan ja toiminnon ja palauttaa uuden tilan.


interface Action {
  type: string;
  payload?: any;
}

function processOrder(state: OrderState, action: Action): OrderState {
  switch (state.type) {
    case "pending":
      if (action.type === "startProcessing") {
        return {
          type: "processing",
          orderId: state.orderId,
          assignedAgent: action.payload.agentId,
        };
      }
      return state; // Ei tilanmuutosta

    case "processing":
      if (action.type === "shipOrder") {
        return {
          type: "shipped",
          orderId: state.orderId,
          trackingNumber: action.payload.trackingNumber,
        };
      }
      return state; // Ei tilanmuutosta

    case "shipped":
      if (action.type === "deliverOrder") {
        return {
          type: "delivered",
          orderId: state.orderId,
          deliveryDate: new Date(),
        };
      }
      return state; // Ei tilanmuutosta

    case "delivered":
      // Tilaus on jo toimitettu, ei lisätoimia
      return state;

    default:
      // Tämän ei pitäisi koskaan tapahtua kattavuuden tarkistuksen ansiosta
      return state; // Tai heitä virhe
  }
}

Selitys

Kattavuuden tarkistuksen hyödyntäminen

TypeScriptin kattavuuden tarkistus on tehokas ominaisuus, joka varmistaa, että käsittelet kaikki mahdolliset tilat tilakoneessasi. Jos lisäät uuden tilan `OrderState`-unioniin mutta unohdat päivittää `processOrder`-funktion, TypeScript ilmoittaa virheestä.

Voit ottaa kattavuuden tarkistuksen käyttöön käyttämällä `never`-tyyppiä. Switch-lauseen `default`-haarassa, määritä tila muuttujalle, jonka tyyppi on `never`.


function processOrder(state: OrderState, action: Action): OrderState {
  switch (state.type) {
    // ... (edelliset haarat) ...

    default:
      const _exhaustiveCheck: never = state;
      return _exhaustiveCheck; // Tai heitä virhe
  }
}

Jos `switch`-lause käsittelee kaikki mahdolliset `OrderState`-arvot, `_exhaustiveCheck`-muuttuja on tyyppiä `never` ja koodi kääntyy. Jos kuitenkin lisäät uuden tilan `OrderState`-unioniin ja unohdat käsitellä sen `switch`-lauseessa, `_exhaustiveCheck`-muuttuja on eri tyyppiä, ja TypeScript heittää käännösaikaisen virheen, mikä ilmoittaa puuttuvasta haarasta.

Käytännön esimerkkejä ja sovelluksia

Discriminated unioneita voidaan soveltaa monenlaisissa skenaarioissa yksinkertaisten tilausten käsittelyjärjestelmien lisäksi:

Esimerkki: Käyttöliittymän tilanhallinta

Tarkastellaan yksinkertaista esimerkkiä käyttöliittymäkomponentin tilan hallinnasta, joka hakee dataa API:sta. Voimme määrittää seuraavat tilat:


interface Initial {
  type: "initial";
}

interface Loading {
  type: "loading";
}

interface Success<T> {
  type: "success";
  data: T;
}

interface Error {
  type: "error";
  message: string;
}

type UIState<T> = Initial | Loading | Success<T> | Error;

function renderUI<T>(state: UIState<T>): React.ReactNode {
  switch (state.type) {
    case "initial":
      return <p>Napsauta painiketta ladataksesi dataa.</p>;
    case "loading":
      return <p>Ladataan...</p>;
    case "success":
      return <pre>{JSON.stringify(state.data, null, 2)}</pre>;
    case "error":
      return <p>Virhe: {state.message}</p>;
    default:
      const _exhaustiveCheck: never = state;
      return _exhaustiveCheck;
  }
}

Tämä esimerkki osoittaa, kuinka discriminated unioneita voidaan käyttää tehokkaasti käyttöliittymäkomponentin eri tilojen hallintaan, varmistaen, että käyttöliittymä renderöidään oikein nykyisen tilan perusteella. `renderUI`-funktio käsittelee jokaisen tilan asianmukaisesti, tarjoten selkeän ja tyypiturvallisen tavan hallita käyttöliittymää.

Parhaat käytännöt Discriminated Unionien käyttöön

Jotta voit hyödyntää discriminated unioneita tehokkaasti TypeScript-projekteissasi, harkitse seuraavia parhaita käytäntöjä:

Edistyneet tekniikat

Ehdolliset tyypit

Ehdolliset tyypit voidaan yhdistää discriminated unioneihin luodakseen entistä tehokkaampia ja joustavampia tilakoneita. Voit esimerkiksi käyttää ehdollisia tyyppejä määrittelemään funktiolle eri paluuarvojen tyyppejä nykyisen tilan perusteella.


function getData<T>(state: UIState<T>): T | undefined {
  if (state.type === "success") {
    return state.data;
  }
  return undefined;
}

Tämä funktio käyttää yksinkertaista `if`-lausetta, mutta siitä voitaisiin tehdä vankempi käyttämällä ehdollisia tyyppejä varmistamaan, että tietty tyyppi palautetaan aina.

Aputyypit (Utility Types)

TypeScriptin aputyypit (utility types), kuten `Extract` ja `Omit`, voivat olla hyödyllisiä työskenneltäessä discriminated unioneiden kanssa. `Extract` antaa sinun poimia tiettyjä jäseniä unioni-tyypistä ehdon perusteella, kun taas `Omit` antaa sinun poistaa ominaisuuksia tyypistä.


// Poimi "success"-tila UIState-unionista
type SuccessState<T> = Extract<UIState<T>, { type: "success" }>;

// Jätä pois 'message'-ominaisuus Error-rajapinnasta
type ErrorWithoutMessage = Omit<Error, "message">;

Tosielämän esimerkkejä eri toimialoilta

Discriminated unionien voima ulottuu useille eri toimialoille ja sovellusalueille:

Yhteenveto

TypeScriptin discriminated unionit tarjoavat tehokkaan ja tyypiturvallisen tavan rakentaa tilakoneita. Määrittelemällä selkeästi mahdolliset tilat ja siirtymät voit luoda vankempaa, ylläpidettävämpää ja ymmärrettävämpää koodia. Tyyppiturvallisuuden, kattavuuden tarkistuksen ja tehostetun koodin täydennyksen yhdistelmä tekee discriminated unioneista korvaamattoman työkalun jokaiselle TypeScript-kehittäjälle, joka käsittelee monimutkaista tilanhallintaa. Ota discriminated unionit käyttöön seuraavassa projektissasi ja koe tyypiturvallisen tilanhallinnan edut omakohtaisesti. Kuten olemme osoittaneet monipuolisilla esimerkeillä verkkokaupasta terveydenhuoltoon ja logistiikasta koulutukseen, tyypiturvallisen tilanhallinnan periaate discriminated unioneiden avulla on yleismaailmallisesti sovellettavissa.

Olitpa rakentamassa yksinkertaista käyttöliittymäkomponenttia tai monimutkaista yrityssovellusta, discriminated unionit voivat auttaa sinua hallitsemaan tilaa tehokkaammin ja vähentämään ajonaikaisten virheiden riskiä. Joten sukella sisään ja tutustu tyypiturvallisten tilakoneiden maailmaan TypeScriptin avulla!