Eesti

Avastage TypeScripti muutumatute andmestruktuuride võimsus readonly-tüüpide abil. Õppige looma ettearvatavamaid, hooldatavamaid ja robustsemaid rakendusi, vältides andmete soovimatuid mutatsioone.

TypeScript'i Readonly tüübid: Muutumatute andmestruktuuride valdamine

Pidevalt areneval tarkvaraarenduse maastikul on robustse, ettearvatava ja hooldatava koodi poole püüdlemine lakkamatu protsess. TypeScript oma tugeva tüübisüsteemiga pakub võimsaid tööriistu nende eesmärkide saavutamiseks. Nende tööriistade seas paistavad readonly tüübid silma kui oluline mehhanism muutumatuse (immutability) jõustamiseks, mis on funktsionaalse programmeerimise nurgakivi ja võti usaldusväärsemate rakenduste loomisel.

Mis on muutumatus ja miks see on oluline?

Muutumatus tähendab oma olemuselt, et kui objekt on loodud, ei saa selle olekut muuta. Sellel lihtsal kontseptsioonil on sügav mõju koodi kvaliteedile ja hooldatavusele.

Readonly tüübid TypeScriptis: Sinu muutumatuse arsenal

TypeScript pakub mitmeid viise muutumatuse jõustamiseks, kasutades readonly märksõna. Uurime erinevaid tehnikaid ja seda, kuidas neid praktikas rakendada.

1. Readonly omadused liidestes ja tüüpides

Kõige otsekohesem viis omaduse readonly'ks deklareerimiseks on kasutada readonly märksõna otse liidese või tüübi definitsioonis.


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

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

// person.id = "new-id"; // Viga: Ei saa omistada 'id'-le, kuna see on kirjutuskaitstud omadus.
person.name = "Bob"; // See on lubatud

Selles näites on id omadus deklareeritud kui readonly. TypeScript takistab kõiki katseid seda pärast objekti loomist muuta. Omadusi name ja age, millel puudub readonly modifikaator, saab vabalt muuta.

2. Readonly abistav tüüp (Utility Type)

TypeScript pakub võimsat abistavat tüüpi nimega Readonly<T>. See geneeriline tüüp võtab olemasoleva tüübi T ja muudab selle, tehes kõik selle omadused readonly'ks.


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

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

// point.x = 30; // Viga: Ei saa omistada 'x'-le, kuna see on kirjutuskaitstud omadus.

Readonly<Point> tüüp loob uue tüübi, kus nii x kui ka y on readonly. See on mugav viis olemasoleva tüübi kiireks muutumatuks tegemiseks.

3. Kirjutuskaitstud massiivid (ReadonlyArray<T>) ja readonly T[]

JavaScripti massiivid on oma olemuselt muutuvad. TypeScript pakub võimalust luua kirjutuskaitstud massiive, kasutades ReadonlyArray<T> tüüpi või lühendit readonly T[]. See takistab massiivi sisu muutmist.


const numbers: ReadonlyArray<number> = [1, 2, 3, 4, 5];
// numbers.push(6); // Viga: Omadust 'push' ei eksisteeri tüübil 'readonly number[]'.
// numbers[0] = 10; // Viga: Indeksi signatuur tüübis 'readonly number[]' lubab ainult lugemist.

const moreNumbers: readonly number[] = [6, 7, 8, 9, 10]; // Samaväärne ReadonlyArray-ga
// moreNumbers.push(11); // Viga: Omadust 'push' ei eksisteeri tüübil 'readonly number[]'.

Katse kasutada massiivi muutvaid meetodeid, nagu push, pop, splice, või otse indeksile väärtuse omistamine, põhjustab TypeScripti vea.

4. const vs readonly: Erinevuse mõistmine

On oluline eristada const ja readonly vahel. const takistab muutuja enda ümberomistamist, samas kui readonly takistab objekti omaduste muutmist. Nad teenivad erinevaid eesmärke ja neid saab kasutada koos maksimaalse muutumatuse saavutamiseks.


const immutableNumber = 42;
// immutableNumber = 43; // Viga: const muutujale 'immutableNumber' ei saa uut väärtust omistada.

const mutableObject = { value: 10 };
mutableObject.value = 20; // See on lubatud, sest *objekt* ise pole const, vaid ainult muutuja.

const readonlyObject: Readonly<{ value: number }> = { value: 30 };
// readonlyObject.value = 40; // Viga: Ei saa omistada 'value'-le, kuna see on kirjutuskaitstud omadus.

const constReadonlyObject: Readonly<{ value: number }> = { value: 50 };
// constReadonlyObject = { value: 60 }; // Viga: const muutujale 'constReadonlyObject' ei saa uut väärtust omistada.
// constReadonlyObject.value = 60; // Viga: Ei saa omistada 'value'-le, kuna see on kirjutuskaitstud omadus.

Nagu ülaltoodud näide demonstreerib, tagab const, et muutuja viitab alati samale objektile mälus, samas kui readonly garanteerib, et objekti sisemine olek jääb muutumatuks.

Praktilised näited: Readonly tüüpide rakendamine reaalsetes stsenaariumides

Uurime mõningaid praktilisi näiteid, kuidas readonly tüüpe saab kasutada koodi kvaliteedi ja hooldatavuse parandamiseks erinevates stsenaariumides.

1. Konfiguratsiooniandmete haldamine

Konfiguratsiooniandmed laaditakse sageli rakenduse käivitamisel üks kord ja neid ei tohiks käitusajal muuta. Readonly tüüpide kasutamine tagab, et need andmed jäävad muutumatuks ja hoiab ära juhuslikud muudatused.


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>) {
    // ... kasuta config.timeout ja config.apiUrl turvaliselt, teades, et need ei muutu
}

fetchData("/data", config);

2. Redux-laadse olekuhalduse rakendamine

Olekuhalduse teekides nagu Redux on muutumatus põhiprintsiip. Readonly tüüpe saab kasutada tagamaks, et olek jääb muutumatuks ja et redutseerijad tagastavad alati uue olekuobjekti, selle asemel et olemasolevat muuta.


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 }; // Tagasta uus olekuobjekt
    case "ADD_ITEM":
      return { ...state, items: [...state.items, action.payload] }; // Tagasta uus olekuobjekt uuendatud elementidega
    default:
      return state;
  }
}

3. API vastustega töötamine

API-st andmete pärimisel on sageli soovitav käsitleda vastuse andmeid muutumatutena, eriti kui kasutate neid kasutajaliidese komponentide renderdamiseks. Readonly tüübid aitavad vältida API andmete juhuslikke mutatsioone.


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; // Viga: Ei saa omistada 'completed'-le, kuna see on kirjutuskaitstud omadus.
});

4. Geograafiliste andmete modelleerimine (rahvusvaheline näide)

Kujutage ette geograafiliste koordinaatide esitamist. Kui koordinaat on määratud, peaks see ideaaljuhul jääma konstantseks. See tagab andmete terviklikkuse, eriti tundlike rakenduste puhul nagu kaardistus- või navigatsioonisüsteemid, mis tegutsevad erinevates geograafilistes piirkondades (nt GPS-koordinaadid kullerteenusele, mis hõlmab Põhja-Ameerikat, Euroopat ja Aasiat).


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 {
 // Kujutle keerulist arvutust laius- ja pikkuskraadide abil
 // Lihtsuse huvides tagastatakse kohatäiteväärtus
 return 1000; 
}

const distance = calculateDistance(tokyoCoordinates, newYorkCoordinates);
console.log("Tokyo ja New Yorgi vaheline kaugus (kohatäide):", distance);

// tokyoCoordinates.latitude = 36.0; // Viga: Ei saa omistada 'latitude'-le, kuna see on kirjutuskaitstud omadus.

Sügavalt kirjutuskaitstud tüübid: Pesastatud objektide käsitlemine

Readonly<T> abistav tüüp muudab ainult objekti otseomadused readonly'ks. Kui objekt sisaldab pesastatud objekte või massiive, jäävad need pesastatud struktuurid muutuvateks. Tõelise sügava muutumatuse saavutamiseks peate rekursiivselt rakendama Readonly<T> kõikidele pesastatud omadustele.

Siin on näide, kuidas luua sügavalt kirjutuskaitstud tüüpi:


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"; // Viga
// company.address.city = "New City"; // Viga
// company.employees.push("Charlie"); // Viga

See DeepReadonly<T> tüüp rakendab rekursiivselt Readonly<T> kõikidele pesastatud omadustele, tagades, et kogu objekti struktuur on muutumatu.

Kaalutlused ja kompromissid

Kuigi muutumatus pakub olulisi eeliseid, on oluline olla teadlik ka võimalikest kompromissidest.

Teegid muutumatute andmestruktuuride jaoks

Mitmed teegid võivad lihtsustada muutumatute andmestruktuuridega töötamist TypeScriptis:

Parimad praktikad Readonly tüüpide kasutamiseks

Et readonly tüüpe oma TypeScripti projektides tõhusalt ära kasutada, järgige neid parimaid praktikaid:

Kokkuvõte: Muutumatuse omaksvõtt TypeScripti Readonly tüüpidega

TypeScripti readonly tüübid on võimas tööriist ettearvatavamate, hooldatavamate ja robustsemate rakenduste loomiseks. Muutumatust omaks võttes saate vähendada vigade riski, lihtsustada silumist ja parandada oma koodi üldist kvaliteeti. Kuigi kaaluda tuleb mõningaid kompromisse, kaaluvad muutumatuse eelised sageli üles kulud, eriti keerukates ja pikaealistes projektides. Jätkates oma TypeScripti teekonda, muutke readonly tüübid oma arendustöövoo keskseks osaks, et avada muutumatuse täielik potentsiaal ja ehitada tõeliselt usaldusväärset tarkvara.