Norsk

Utforsk TypeScript literal-typer, en kraftig funksjon for å håndheve strenge verdibegrensninger, forbedre kodens lesbarhet og forhindre feil. Lær med praktiske eksempler og avanserte teknikker.

TypeScript Literal-typer: Mestre eksakte verdibegrensninger

TypeScript, et supersett av JavaScript, bringer statisk typing til den dynamiske verdenen av webutvikling. En av de kraftigste funksjonene er konseptet med literal-typer. Literal-typer lar deg spesifisere den nøyaktige verdien en variabel eller egenskap kan ha, noe som gir økt typesikkerhet og forhindrer uventede feil. Denne artikkelen vil utforske literal-typer i dybden, og dekke deres syntaks, bruk og fordeler med praktiske eksempler.

Hva er Literal-typer?

I motsetning til tradisjonelle typer som string, number, eller boolean, representerer ikke literal-typer en bred kategori av verdier. I stedet representerer de spesifikke, faste verdier. TypeScript støtter tre typer literal-typer:

Ved å bruke literal-typer kan du lage mer presise typedefinisjoner som gjenspeiler de faktiske begrensningene i dataene dine, noe som fører til mer robust og vedlikeholdbar kode.

String Literal-typer

String literal-typer er den mest brukte typen literal. De lar deg spesifisere at en variabel eller egenskap kun kan inneholde én av et forhåndsdefinert sett med strengverdier.

Grunnleggende syntaks

Syntaksen for å definere en string literal-type er enkel:


type TillatteVerdier = "verdi1" | "verdi2" | "verdi3";

Dette definerer en type ved navn TillatteVerdier som kun kan inneholde strengene "verdi1", "verdi2", eller "verdi3".

Praktiske eksempler

1. Definere en fargepalett:

Tenk deg at du bygger et UI-bibliotek og vil sikre at brukere kun kan spesifisere farger fra en forhåndsdefinert palett:


type Farge = "red" | "green" | "blue" | "yellow";

function malElement(element: HTMLElement, farge: Farge) {
  element.style.backgroundColor = farge;
}

malElement(document.getElementById("myElement")!, "red"); // Gyldig
malElement(document.getElementById("myElement")!, "purple"); // Feil: Argumentet av typen '"purple"' kan ikke tilordnes parameter av typen 'Farge'.

Dette eksempelet viser hvordan string literal-typer kan håndheve et strengt sett med tillatte verdier, og forhindre at utviklere ved et uhell bruker ugyldige farger.

2. Definere API-endepunkter:

Når du jobber med API-er, må du ofte spesifisere de tillatte endepunktene. String literal-typer kan hjelpe med å håndheve dette:


type APIEndepunkt = "/users" | "/posts" | "/comments";

function hentData(endepunkt: APIEndepunkt) {
  // ... implementasjon for å hente data fra det spesifiserte endepunktet
  console.log(`Henter data fra ${endepunkt}`);
}

hentData("/users"); // Gyldig
hentData("/products"); // Feil: Argumentet av typen '"/products"' kan ikke tilordnes parameter av typen 'APIEndepunkt'.

Dette eksemplet sikrer at hentData-funksjonen kun kan kalles med gyldige API-endepunkter, noe som reduserer risikoen for feil forårsaket av skrivefeil eller feil endepunktnavn.

3. Håndtere forskjellige språk (Internasjonalisering - i18n):

I globale applikasjoner kan det være nødvendig å håndtere forskjellige språk. Du kan bruke string literal-typer for å sikre at applikasjonen din kun støtter de spesifiserte språkene:


type Språk = "en" | "es" | "fr" | "de" | "zh";

function oversett(tekst: string, språk: Språk): string {
  // ... implementasjon for å oversette teksten til det spesifiserte språket
  console.log(`Oversetter '${tekst}' til ${språk}`);
  return "Oversatt tekst"; // Plassholder
}

oversett("Hello", "en"); // Gyldig
oversett("Hello", "ja"); // Feil: Argumentet av typen '"ja"' kan ikke tilordnes parameter av typen 'Språk'.

Dette eksemplet viser hvordan man kan sikre at kun støttede språk brukes i applikasjonen.

Nummer Literal-typer

Nummer literal-typer lar deg spesifisere at en variabel eller egenskap kun kan inneholde en spesifikk numerisk verdi.

Grunnleggende syntaks

Syntaksen for å definere en nummer literal-type ligner på string literal-typer:


type Statuskode = 200 | 404 | 500;

Dette definerer en type ved navn Statuskode som kun kan inneholde tallene 200, 404, eller 500.

Praktiske eksempler

1. Definere HTTP-statuskoder:

Du kan bruke nummer literal-typer til å representere HTTP-statuskoder, og sikre at kun gyldige koder brukes i applikasjonen din:


type HTTPStatus = 200 | 400 | 401 | 403 | 404 | 500;

function håndterRespons(status: HTTPStatus) {
  switch (status) {
    case 200:
      console.log("Suksess!");
      break;
    case 400:
      console.log("Bad Request");
      break;
    // ... andre tilfeller
    default:
      console.log("Ukjent Status");
  }
}

håndterRespons(200); // Gyldig
håndterRespons(600); // Feil: Argumentet av typen '600' kan ikke tilordnes parameter av typen 'HTTPStatus'.

Dette eksemplet håndhever bruken av gyldige HTTP-statuskoder, og forhindrer feil forårsaket av bruk av feilaktige eller ikke-standardiserte koder.

2. Representere faste alternativer:

Du kan bruke nummer literal-typer til å representere faste alternativer i et konfigurasjonsobjekt:


type AntallForsøk = 1 | 3 | 5;

interface Config {
  antallForsøk: AntallForsøk;
}

const config1: Config = { antallForsøk: 3 }; // Gyldig
const config2: Config = { antallForsøk: 7 }; // Feil: Typen '{ antallForsøk: 7; }' kan ikke tilordnes typen 'Config'.

Dette eksemplet begrenser de mulige verdiene for antallForsøk til et spesifikt sett, noe som forbedrer klarheten og påliteligheten til konfigurasjonen din.

Boolean Literal-typer

Boolean literal-typer representerer de spesifikke verdiene true eller false. Selv om de kan virke mindre allsidige enn string- eller nummer-literal-typer, kan de være nyttige i spesifikke scenarier.

Grunnleggende syntaks

Syntaksen for å definere en boolean literal-type er:


type ErAktivert = true | false;

Imidlertid er det overflødig å bruke true | false direkte, fordi det er ekvivalent med boolean-typen. Boolean literal-typer er mer nyttige når de kombineres med andre typer eller i betingede typer.

Praktiske eksempler

1. Betinget logikk med konfigurasjon:

Du kan bruke boolean literal-typer til å kontrollere oppførselen til en funksjon basert på et konfigurasjonsflagg:


interface FeatureFlags {
  darkMode: boolean;
  newUserFlow: boolean;
}

function initialiserApp(flags: FeatureFlags) {
  if (flags.darkMode) {
    // Aktiver mørk modus
    console.log("Aktiverer mørk modus...");
  } else {
    // Bruk lys modus
    console.log("Bruker lys modus...");
  }

  if (flags.newUserFlow) {
    // Aktiver ny brukerflyt
    console.log("Aktiverer ny brukerflyt...");
  } else {
    // Bruk gammel brukerflyt
    console.log("Bruker gammel brukerflyt...");
  }
}

initialiserApp({ darkMode: true, newUserFlow: false });

Selv om dette eksemplet bruker standard boolean-typen, kan du kombinere den med betingede typer (forklart senere) for å skape mer kompleks oppførsel.

2. Diskriminerte unioner:

Boolean literal-typer kan brukes som diskriminatorer i union-typer. Vurder følgende eksempel:


interface SuksessResultat {
  suksess: true;
  data: any;
}

interface FeilResultat {
  suksess: false;
  feil: string;
}

type Resultat = SuksessResultat | FeilResultat;

function prosesserResultat(resultat: Resultat) {
  if (resultat.suksess) {
    console.log("Suksess:", resultat.data);
  } else {
    console.error("Feil:", resultat.feil);
  }
}

prosesserResultat({ suksess: true, data: { navn: "John" } });
prosesserResultat({ suksess: false, feil: "Kunne ikke hente data" });

Her fungerer suksess-egenskapen, som er en boolean literal-type, som en diskriminator, noe som lar TypeScript innsnevre typen av resultat innenfor if-setningen.

Kombinere Literal-typer med Union-typer

Literal-typer er mest effektive når de kombineres med union-typer (ved hjelp av |-operatoren). Dette lar deg definere en type som kan inneholde én av flere spesifikke verdier.

Praktiske eksempler

1. Definere en statustype:


type Status = "pending" | "in progress" | "completed" | "failed";

interface Oppgave {
  id: number;
  beskrivelse: string;
  status: Status;
}

const oppgave1: Oppgave = { id: 1, beskrivelse: "Implementer innlogging", status: "in progress" }; // Gyldig
const oppgave2: Oppgave = { id: 2, beskrivelse: "Implementer utlogging", status: "done" };       // Feil: Typen '{ id: number; beskrivelse: string; status: string; }' kan ikke tilordnes typen 'Oppgave'.

Dette eksemplet viser hvordan man kan håndheve et spesifikt sett med tillatte statusverdier for et Oppgave-objekt.

2. Definere en enhetstype:

I en mobilapplikasjon kan det være nødvendig å håndtere forskjellige enhetstyper. Du kan bruke en union av string literal-typer for å representere disse:


type Enhetstype = "mobile" | "tablet" | "desktop";

function loggEnhetstype(enhet: Enhetstype) {
  console.log(`Enhetstype: ${enhet}`);
}

loggEnhetstype("mobile"); // Gyldig
loggEnhetstype("smartwatch"); // Feil: Argumentet av typen '"smartwatch"' kan ikke tilordnes parameter av typen 'Enhetstype'.

Dette eksemplet sikrer at loggEnhetstype-funksjonen kun kalles med gyldige enhetstyper.

Literal-typer med Typealiaser

Typealiaser (ved hjelp av type-nøkkelordet) gir en måte å gi et navn til en literal-type, noe som gjør koden din mer lesbar og vedlikeholdbar.

Praktiske eksempler

1. Definere en valutakodetype:


type Valutakode = "USD" | "EUR" | "GBP" | "JPY";

function formaterValuta(beløp: number, valuta: Valutakode): string {
  // ... implementasjon for å formatere beløpet basert på valutakoden
  console.log(`Formaterer ${beløp} i ${valuta}`);
  return "Formatert beløp"; // Plassholder
}

formaterValuta(100, "USD"); // Gyldig
formaterValuta(200, "CAD"); // Feil: Argumentet av typen '"CAD"' kan ikke tilordnes parameter av typen 'Valutakode'.

Dette eksemplet definerer en Valutakode-typealias for et sett med valutakoder, noe som forbedrer lesbarheten til formaterValuta-funksjonen.

2. Definere en ukedagstype:


type Ukedag = "Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday" | "Saturday" | "Sunday";

function erHelg(dag: Ukedag): boolean {
  return dag === "Saturday" || dag === "Sunday";
}

console.log(erHelg("Monday"));   // false
console.log(erHelg("Saturday")); // true
console.log(erHelg("Funday"));   // Feil: Argumentet av typen '"Funday"' kan ikke tilordnes parameter av typen 'Ukedag'.

Literal-inferens

TypeScript kan ofte utlede (infer) literal-typer automatisk basert på verdiene du tilordner til variabler. Dette er spesielt nyttig når du jobber med const-variabler.

Praktiske eksempler

1. Utlede String Literal-typer:


const apiKey = "your-api-key"; // TypeScript utleder typen til apiKey som "your-api-key"

function validerApiKey(nøkkel: "your-api-key") {
  return nøkkel === "your-api-key";
}

console.log(validerApiKey(apiKey)); // true

const enAnnenNøkkel = "invalid-key";
console.log(validerApiKey(enAnnenNøkkel)); // Feil: Argumentet av typen 'string' kan ikke tilordnes parameter av typen '"your-api-key"'.

I dette eksemplet utleder TypeScript typen til apiKey som string literal-typen "your-api-key". Men hvis du tilordner en ikke-konstant verdi til en variabel, vil TypeScript vanligvis utlede den bredere string-typen.

2. Utlede Nummer Literal-typer:


const port = 8080; // TypeScript utleder typen til port som 8080

function startServer(portNummer: 8080) {
  console.log(`Starter server på port ${portNummer}`);
}

startServer(port); // Gyldig

const enAnnenPort = 3000;
startServer(enAnnenPort); // Feil: Argumentet av typen 'number' kan ikke tilordnes parameter av typen '8080'.

Bruke Literal-typer med Betingede typer

Literal-typer blir enda kraftigere når de kombineres med betingede typer. Betingede typer lar deg definere typer som avhenger av andre typer, og skaper svært fleksible og uttrykksfulle typesystemer.

Grunnleggende syntaks

Syntaksen for en betinget type er:


TypeA extends TypeB ? TypeC : TypeD

Dette betyr: hvis TypeA kan tilordnes TypeB, er den resulterende typen TypeC; ellers er den resulterende typen TypeD.

Praktiske eksempler

1. Kartlegge status til melding:


type Status = "pending" | "in progress" | "completed" | "failed";

type StatusMelding = T extends "pending"
  ? "Venter på handling"
  : T extends "in progress"
  ? "Behandles for øyeblikket"
  : T extends "completed"
  ? "Oppgaven er fullført"
  : "En feil har oppstått";

function hentStatusMelding(status: T): StatusMelding {
  switch (status) {
    case "pending":
      return "Venter på handling" as StatusMelding;
    case "in progress":
      return "Behandles for øyeblikket" as StatusMelding;
    case "completed":
      return "Oppgaven er fullført" as StatusMelding;
    case "failed":
      return "En feil har oppstått" as StatusMelding;
    default:
      throw new Error("Ugyldig status");
  }
}

console.log(hentStatusMelding("pending"));    // Venter på handling
console.log(hentStatusMelding("in progress")); // Behandles for øyeblikket
console.log(hentStatusMelding("completed"));   // Oppgaven er fullført
console.log(hentStatusMelding("failed"));      // En feil har oppstått

Dette eksemplet definerer en StatusMelding-type som kartlegger hver mulig status til en tilsvarende melding ved hjelp av betingede typer. Funksjonen hentStatusMelding utnytter denne typen for å gi typesikre statusmeldinger.

2. Lage en typesikker hendelsesbehandler:


type Hendelsestype = "click" | "mouseover" | "keydown";

type Hendelsesdata = T extends "click"
  ? { x: number; y: number; } // Klikk-hendelsesdata
  : T extends "mouseover"
  ? { target: HTMLElement; }   // Mouseover-hendelsesdata
  : { key: string; }             // Keydown-hendelsesdata

function håndterHendelse(type: T, data: Hendelsesdata) {
  console.log(`Håndterer hendelsestype ${type} med data:`, data);
}

håndterHendelse("click", { x: 10, y: 20 }); // Gyldig
håndterHendelse("mouseover", { target: document.getElementById("myElement")! }); // Gyldig
håndterHendelse("keydown", { key: "Enter" }); // Gyldig

håndterHendelse("click", { key: "Enter" }); // Feil: Argumentet av typen '{ key: string; }' kan ikke tilordnes parameter av typen '{ x: number; y: number; }'.

Dette eksemplet lager en Hendelsesdata-type som definerer forskjellige datastrukturer basert på hendelsestypen. Dette lar deg sikre at riktig data blir sendt til håndterHendelse-funksjonen for hver hendelsestype.

Beste praksis for bruk av Literal-typer

For å bruke literal-typer effektivt i dine TypeScript-prosjekter, bør du vurdere følgende beste praksis:

Fordeler med å bruke Literal-typer

Konklusjon

TypeScript literal-typer er en kraftig funksjon som lar deg håndheve strenge verdibegrensninger, forbedre kodens lesbarhet og forhindre feil. Ved å forstå deres syntaks, bruk og fordeler, kan du utnytte literal-typer for å lage mer robuste og vedlikeholdbare TypeScript-applikasjoner. Fra å definere fargepaletter og API-endepunkter til å håndtere forskjellige språk og lage typesikre hendelsesbehandlere, tilbyr literal-typer et bredt spekter av praktiske anvendelser som kan forbedre utviklingsflyten din betydelig.

TypeScript Literal-typer: Mestre eksakte verdibegrensninger | MLOG