Norsk

Frigjør kraften i uforanderlige datastrukturer i TypeScript med readonly-typer. Lær hvordan du lager mer forutsigbare, vedlikeholdbare og robuste applikasjoner ved å forhindre utilsiktede datamutasjoner.

TypeScript Readonly-typer: Mestre uforanderlige datastrukturer

I det stadig utviklende landskapet innen programvareutvikling, er jakten på robust, forutsigbar og vedlikeholdbar kode en konstant bestrebelse. TypeScript, med sitt sterke typesystem, gir kraftige verktøy for å oppnå disse målene. Blant disse verktøyene skiller readonly-typer seg ut som en avgjørende mekanisme for å håndheve uforanderlighet, en hjørnestein i funksjonell programmering og en nøkkel til å bygge mer pålitelige applikasjoner.

Hva er uforanderlighet og hvorfor er det viktig?

Uforanderlighet, i sin kjerne, betyr at når et objekt er opprettet, kan tilstanden dets ikke endres. Dette enkle konseptet har dype implikasjoner for kodekvalitet og vedlikeholdbarhet.

Readonly-typer i TypeScript: Ditt arsenal for uforanderlighet

TypeScript tilbyr flere måter å håndheve uforanderlighet på ved å bruke nøkkelordet readonly. La oss utforske de forskjellige teknikkene og hvordan de kan brukes i praksis.

1. Readonly-egenskaper på grensesnitt og typer

Den mest direkte måten å deklarere en egenskap som readonly på er å bruke nøkkelordet readonly direkte i et grensesnitt eller en typedefinisjon.


interface Person {
  readonly id: string;
  name: string;
  age: number;
}

const person: Person = {
  id: "unique-id-123",
  name: "Alice",
  age: 30,
};

// person.id = "new-id"; // Feil: Kan ikke tilordne til 'id' fordi det er en skrivebeskyttet egenskap.
person.name = "Bob"; // Dette er tillatt

I dette eksempelet er id-egenskapen deklarert som readonly. TypeScript vil forhindre alle forsøk på å endre den etter at objektet er opprettet. Egenskapene name og age, som mangler readonly-modifikatoren, kan endres fritt.

2. Verktøytypen Readonly

TypeScript tilbyr en kraftig verktøytype kalt Readonly<T>. Denne generiske typen tar en eksisterende type T og transformerer den ved å gjøre alle egenskapene dens readonly.


interface Point {
  x: number;
  y: number;
}

const point: Readonly<Point> = {
  x: 10,
  y: 20,
};

// point.x = 30; // Feil: Kan ikke tilordne til 'x' fordi det er en skrivebeskyttet egenskap.

Typen Readonly<Point> oppretter en ny type der både x og y er readonly. Dette er en praktisk måte å raskt gjøre en eksisterende type uforanderlig på.

3. Readonly-arrays (ReadonlyArray<T>) og readonly T[]

Arrays i JavaScript er i sin natur foranderlige. TypeScript gir en måte å lage readonly-arrays på ved å bruke typen ReadonlyArray<T> eller kortformen readonly T[]. Dette forhindrer endring av arrayets innhold.


const numbers: ReadonlyArray<number> = [1, 2, 3, 4, 5];
// numbers.push(6); // Feil: Egenskapen 'push' finnes ikke på typen 'readonly number[]'.
// numbers[0] = 10; // Feil: Indekssignatur i typen 'readonly number[]' tillater kun lesing.

const moreNumbers: readonly number[] = [6, 7, 8, 9, 10]; // Ekvivalent med ReadonlyArray
// moreNumbers.push(11); // Feil: Egenskapen 'push' finnes ikke på typen 'readonly number[]'.

Forsøk på å bruke metoder som endrer arrayet, som push, pop, splice, eller å tildele direkte til en indeks, vil resultere i en TypeScript-feil.

4. const vs. readonly: Forstå forskjellen

Det er viktig å skille mellom const og readonly. const forhindrer re-tilordning av selve variabelen, mens readonly forhindrer endring av objektets egenskaper. De tjener forskjellige formål og kan brukes sammen for maksimal uforanderlighet.


const immutableNumber = 42;
// immutableNumber = 43; // Feil: Kan ikke tilordne på nytt til const-variabelen 'immutableNumber'.

const mutableObject = { value: 10 };
mutableObject.value = 20; // Dette er tillatt fordi *objektet* ikke er const, kun variabelen.

const readonlyObject: Readonly<{ value: number }> = { value: 30 };
// readonlyObject.value = 40; // Feil: Kan ikke tilordne til 'value' fordi det er en skrivebeskyttet egenskap.

const constReadonlyObject: Readonly<{ value: number }> = { value: 50 };
// constReadonlyObject = { value: 60 }; // Feil: Kan ikke tilordne på nytt til const-variabelen 'constReadonlyObject'.
// constReadonlyObject.value = 60; // Feil: Kan ikke tilordne til 'value' fordi det er en skrivebeskyttet egenskap.

Som vist ovenfor, sikrer const at variabelen alltid peker til det samme objektet i minnet, mens readonly garanterer at objektets interne tilstand forblir uendret.

Praktiske eksempler: Bruk av Readonly-typer i virkelige scenarioer

La oss utforske noen praktiske eksempler på hvordan readonly-typer kan brukes for å forbedre kodekvalitet og vedlikeholdbarhet i ulike scenarioer.

1. Håndtering av konfigurasjonsdata

Konfigurasjonsdata lastes ofte én gang ved oppstart av applikasjonen og bør ikke endres under kjøring. Bruk av readonly-typer sikrer at disse dataene forblir konsistente og forhindrer utilsiktede endringer.


interface AppConfig {
  readonly apiUrl: string;
  readonly timeout: number;
  readonly features: readonly string[];
}

const config: AppConfig = {
  apiUrl: "https://api.example.com",
  timeout: 5000,
  features: ["featureA", "featureB"],
};

function fetchData(url: string, config: Readonly<AppConfig>) {
    // ... bruk config.timeout og config.apiUrl trygt, vel vitende om at de ikke vil endre seg
}

fetchData("/data", config);

2. Implementering av Redux-lignende tilstandshåndtering

I tilstandshåndteringsbiblioteker som Redux, er uforanderlighet et kjerneprinsipp. Readonly-typer kan brukes for å sikre at tilstanden forblir uforanderlig og at reducere kun returnerer nye tilstandsobjekter i stedet for å endre de eksisterende.


interface State {
  readonly count: number;
  readonly items: readonly string[];
}

const initialState: State = {
  count: 0,
  items: [],
};

function reducer(state: Readonly<State>, action: { type: string; payload?: any }): State {
  switch (action.type) {
    case "INCREMENT":
      return { ...state, count: state.count + 1 }; // Returner et nytt tilstandsobjekt
    case "ADD_ITEM":
      return { ...state, items: [...state.items, action.payload] }; // Returner et nytt tilstandsobjekt med oppdaterte elementer
    default:
      return state;
  }
}

3. Arbeide med API-responser

Når man henter data fra et API, er det ofte ønskelig å behandle responsdataene som uforanderlige, spesielt hvis du bruker dem til å gjengi UI-komponenter. Readonly-typer kan bidra til å forhindre utilsiktede mutasjoner av API-dataene.


interface ApiResponse {
  readonly userId: number;
  readonly id: number;
  readonly title: string;
  readonly completed: boolean;
}

async function fetchTodo(id: number): Promise<Readonly<ApiResponse>> {
  const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${id}`);
  const data: ApiResponse = await response.json();
  return data;
}

fetchTodo(1).then(todo => {
  console.log(todo.title);
  // todo.completed = true; // Feil: Kan ikke tilordne til 'completed' fordi det er en skrivebeskyttet egenskap.
});

4. Modellering av geografiske data (internasjonalt eksempel)

Vurder å representere geografiske koordinater. Når en koordinat er satt, bør den ideelt sett forbli konstant. Dette sikrer dataintegritet, spesielt når man håndterer sensitive applikasjoner som kartleggings- eller navigasjonssystemer som opererer på tvers av forskjellige geografiske regioner (f.eks. GPS-koordinater for en leveringstjeneste som spenner over Nord-Amerika, Europa og Asia).


interface GeoCoordinates {
 readonly latitude: number;
 readonly longitude: number;
}

const tokyoCoordinates: GeoCoordinates = {
 latitude: 35.6895,
 longitude: 139.6917
};

const newYorkCoordinates: GeoCoordinates = {
 latitude: 40.7128,
 longitude: -74.0060
};


function calculateDistance(coord1: Readonly<GeoCoordinates>, coord2: Readonly<GeoCoordinates>): number {
 // Tenk deg en kompleks beregning med bredde- og lengdegrad
 // Returnerer en plassholderverdi for enkelhets skyld
 return 1000; 
}

const distance = calculateDistance(tokyoCoordinates, newYorkCoordinates);
console.log("Avstand mellom Tokyo og New York (plassholder):", distance);

// tokyoCoordinates.latitude = 36.0; // Feil: Kan ikke tilordne til 'latitude' fordi det er en skrivebeskyttet egenskap.

Dypt skrivebeskyttede typer: Håndtering av nøstede objekter

Verktøytypen Readonly<T> gjør kun de direkte egenskapene til et objekt readonly. Hvis et objekt inneholder nøstede objekter eller arrays, forblir disse nøstede strukturene foranderlige. For å oppnå ekte dyp uforanderlighet, må du rekursivt anvende Readonly<T> på alle nøstede egenskaper.

Her er et eksempel på hvordan man lager en dypt skrivebeskyttet type:


type DeepReadonly<T> = T extends (infer R)[]
  ? DeepReadonlyArray<R>
  : T extends object
  ? DeepReadonlyObject<T>
  : T;

interface DeepReadonlyArray<T> extends ReadonlyArray<DeepReadonly<T>> {}

type DeepReadonlyObject<T> = {
  readonly [P in keyof T]: DeepReadonly<T[P]>;
};

interface Company {
  name: string;
  address: {
    street: string;
    city: string;
    country: string;
  };
  employees: string[];
}

const company: DeepReadonly<Company> = {
  name: "Example Corp",
  address: {
    street: "123 Main St",
    city: "Anytown",
    country: "USA",
  },
  employees: ["Alice", "Bob"],
};

// company.name = "New Corp"; // Feil
// company.address.city = "New City"; // Feil
// company.employees.push("Charlie"); // Feil

Denne DeepReadonly<T>-typen anvender Readonly<T> rekursivt på alle nøstede egenskaper, og sikrer at hele objektstrukturen er uforanderlig.

Vurderinger og avveininger

Selv om uforanderlighet gir betydelige fordeler, er det viktig å være klar over de potensielle avveiningene.

Biblioteker for uforanderlige datastrukturer

Flere biblioteker kan forenkle arbeidet med uforanderlige datastrukturer i TypeScript:

Beste praksis for bruk av Readonly-typer

For å effektivt utnytte readonly-typer i dine TypeScript-prosjekter, følg disse beste praksisene:

Konklusjon: Omfavne uforanderlighet med TypeScript Readonly-typer

TypeScripts readonly-typer er et kraftig verktøy for å bygge mer forutsigbare, vedlikeholdbare og robuste applikasjoner. Ved å omfavne uforanderlighet kan du redusere risikoen for feil, forenkle feilsøking og forbedre den generelle kvaliteten på koden din. Selv om det er noen avveininger å vurdere, veier fordelene med uforanderlighet ofte opp for kostnadene, spesielt i komplekse og langvarige prosjekter. Mens du fortsetter din TypeScript-reise, gjør readonly-typer til en sentral del av arbeidsflyten din for å frigjøre det fulle potensialet til uforanderlighet og bygge virkelig pålitelig programvare.

TypeScript Readonly-typer: Mestre uforanderlige datastrukturer for robuste applikasjoner | MLOG