Lietuvių

Atskleiskite nekintančių duomenų struktūrų galią TypeScript'e su readonly tipais. Sužinokite, kaip kurti nuspėjamesnes ir patikimesnes programas, išvengiant netyčinių duomenų mutacijų.

TypeScript Readonly tipai: nekintamų duomenų struktūrų įvaldymas

Nuolat besikeičiančiame programinės įrangos kūrimo pasaulyje, siekis sukurti patikimą, nuspėjamą ir lengvai palaikomą kodą yra nuolatinis iššūkis. TypeScript, su savo stipria tipų sistema, suteikia galingus įrankius šiems tikslams pasiekti. Tarp šių įrankių, readonly tipai išsiskiria kaip esminis mechanizmas nekintamumui (immutability) užtikrinti – tai funkcinio programavimo kertinis akmuo ir raktas į patikimesnių programų kūrimą.

Kas yra nekintamumas ir kodėl tai svarbu?

Nekintamumas iš esmės reiškia, kad sukūrus objektą, jo būsena negali būti pakeista. Ši paprasta koncepcija turi didžiulę įtaką kodo kokybei ir palaikomumui.

Readonly tipai TypeScript'e: Jūsų nekintamumo arsenalas

TypeScript suteikia keletą būdų įgyvendinti nekintamumą naudojant readonly raktinį žodį. Panagrinėkime skirtingus metodus ir kaip juos galima pritaikyti praktiškai.

1. Readonly savybės sąsajose ir tipuose

Paprasčiausias būdas deklaruoti savybę kaip readonly yra tiesiogiai naudoti readonly raktinį žodį sąsajos ar tipo apibrėžime.


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

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

// person.id = "new-id"; // Klaida: Negalima priskirti 'id', nes tai yra tik skaitoma savybė.
person.name = "Bob"; // Tai leidžiama

Šiame pavyzdyje id savybė yra deklaruota kaip readonly. TypeScript neleis bandyti jos modifikuoti po objekto sukūrimo. Savybės name ir age, neturinčios readonly modifikatoriaus, gali būti laisvai keičiamos.

2. Readonly pagalbinis tipas

TypeScript siūlo galingą pagalbinį tipą, vadinamą Readonly<T>. Šis bendrinis tipas paima esamą tipą T ir jį transformuoja, paversdamas visas jo savybes readonly.


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

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

// point.x = 30; // Klaida: Negalima priskirti 'x', nes tai yra tik skaitoma savybė.

Readonly<Point> tipas sukuria naują tipą, kuriame tiek x, tiek y yra readonly. Tai patogus būdas greitai padaryti esamą tipą nekintamu.

3. Readonly masyvai (ReadonlyArray<T>) ir readonly T[]

JavaScript masyvai iš prigimties yra kintami. TypeScript suteikia būdą sukurti readonly masyvus naudojant ReadonlyArray<T> tipą arba trumpesnį variantą readonly T[]. Tai apsaugo nuo masyvo turinio keitimo.


const numbers: ReadonlyArray<number> = [1, 2, 3, 4, 5];
// numbers.push(6); // Klaida: Savybė 'push' neegzistuoja 'readonly number[]' tipe.
// numbers[0] = 10; // Klaida: Indekso signatūra 'readonly number[]' tipe leidžia tik skaitymą.

const moreNumbers: readonly number[] = [6, 7, 8, 9, 10]; // Atitinka ReadonlyArray
// moreNumbers.push(11); // Klaida: Savybė 'push' neegzistuoja 'readonly number[]' tipe.

Bandant naudoti metodus, kurie keičia masyvą, tokius kaip push, pop, splice, ar tiesiogiai priskiriant reikšmę indeksui, TypeScript'as pateiks klaidą.

4. const vs. readonly: Skirtumo supratimas

Svarbu atskirti const nuo readonly. const neleidžia iš naujo priskirti reikšmės pačiam kintamajam, o readonly neleidžia modifikuoti objekto savybių. Jie atlieka skirtingas funkcijas ir gali būti naudojami kartu siekiant maksimalaus nekintamumo.


const immutableNumber = 42;
// immutableNumber = 43; // Klaida: Negalima iš naujo priskirti const kintamajam 'immutableNumber'.

const mutableObject = { value: 10 };
mutableObject.value = 20; // Tai leidžiama, nes pats *objektas* nėra const, o tik kintamasis.

const readonlyObject: Readonly<{ value: number }> = { value: 30 };
// readonlyObject.value = 40; // Klaida: Negalima priskirti 'value', nes tai yra tik skaitoma savybė.

const constReadonlyObject: Readonly<{ value: number }> = { value: 50 };
// constReadonlyObject = { value: 60 }; // Klaida: Negalima iš naujo priskirti const kintamajam 'constReadonlyObject'.
// constReadonlyObject.value = 60; // Klaida: Negalima priskirti 'value', nes tai yra tik skaitoma savybė.

Kaip parodyta aukščiau, const užtikrina, kad kintamasis visada rodytų į tą patį objektą atmintyje, o readonly garantuoja, kad objekto vidinė būsena išliks nepakitusi.

Praktiniai pavyzdžiai: Readonly tipų taikymas realaus pasaulio scenarijuose

Panagrinėkime keletą praktinių pavyzdžių, kaip readonly tipai gali būti naudojami siekiant pagerinti kodo kokybę ir palaikomumą įvairiuose scenarijuose.

1. Konfigūracijos duomenų valdymas

Konfigūracijos duomenys dažnai įkeliami vieną kartą, programos paleidimo metu, ir neturėtų būti keičiami vykdymo metu. Readonly tipų naudojimas užtikrina, kad šie duomenys išliks nuoseklūs ir apsaugo nuo atsitiktinių pakeitimų.


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>) {
    // ... saugiai naudokite config.timeout ir config.apiUrl, žinodami, kad jie nepasikeis
}

fetchData("/data", config);

2. Įgyvendinant Redux tipo būsenos valdymą

Būsenos valdymo bibliotekose, tokiose kaip Redux, nekintamumas yra pagrindinis principas. Readonly tipai gali būti naudojami užtikrinti, kad būsena išliktų nekintama, o reduktoriai (reducers) grąžintų tik naujus būsenos objektus, o ne modifikuotų esamus.


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 }; // Grąžinti naują būsenos objektą
    case "ADD_ITEM":
      return { ...state, items: [...state.items, action.payload] }; // Grąžinti naują būsenos objektą su atnaujintais elementais
    default:
      return state;
  }
}

3. Darbas su API atsakymais

Gaunant duomenis iš API, dažnai yra pageidautina laikyti atsakymo duomenis nekintamais, ypač jei juos naudojate vartotojo sąsajos komponentų atvaizdavimui. Readonly tipai gali padėti išvengti atsitiktinių API duomenų mutacijų.


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; // Klaida: Negalima priskirti 'completed', nes tai yra tik skaitoma savybė.
});

4. Geografinių duomenų modeliavimas (Tarptautinis pavyzdys)

Apsvarstykite geografinių koordinačių vaizdavimą. Nustačius koordinates, jos idealiu atveju turėtų likti pastovios. Tai užtikrina duomenų vientisumą, ypač dirbant su jautriomis programomis, tokiomis kaip žemėlapių ar navigacijos sistemos, veikiančios skirtinguose geografiniuose regionuose (pvz., GPS koordinatės pristatymo tarnybai, apimančiai Šiaurės Ameriką, Europą ir Aziją).


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 {
 // Įsivaizduokite sudėtingą skaičiavimą naudojant platumą ir ilgumą
 // Grąžinama laikina reikšmė paprastumo dėlei
 return 1000; 
}

const distance = calculateDistance(tokyoCoordinates, newYorkCoordinates);
console.log("Atstumas tarp Tokijo ir Niujorko (laikina reikšmė):", distance);

// tokyoCoordinates.latitude = 36.0; // Klaida: Negalima priskirti 'latitude', nes tai yra tik skaitoma savybė.

Giliai Readonly tipai: Įdėtųjų objektų valdymas

Pagalbinis tipas Readonly<T> padaro readonly tik tiesiogines objekto savybes. Jei objektas turi įdėtųjų objektų ar masyvų, tos įdėtosios struktūros išlieka kintamos. Norint pasiekti tikrą gilųjį nekintamumą, reikia rekursyviai taikyti Readonly<T> visoms įdėtoms savybėms.

Štai pavyzdys, kaip sukurti giliai readonly tipą:


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

Šis DeepReadonly<T> tipas rekursyviai taiko Readonly<T> visoms įdėtoms savybėms, užtikrindamas, kad visa objekto struktūra būtų nekintama.

Svarstymai ir kompromisai

Nors nekintamumas siūlo didelių privalumų, svarbu žinoti apie galimus kompromisus.

Bibliotekos nekintamoms duomenų struktūroms

Kelios bibliotekos gali supaprastinti darbą su nekintamomis duomenų struktūromis TypeScript'e:

Gerosios praktikos naudojant Readonly tipus

Norėdami efektyviai išnaudoti readonly tipus savo TypeScript projektuose, laikykitės šių gerųjų praktikų:

Išvada: Nekintamumo priėmimas su TypeScript Readonly tipais

TypeScript readonly tipai yra galingas įrankis, padedantis kurti labiau nuspėjamas, lengviau palaikomas ir patikimesnes programas. Priimdami nekintamumą, galite sumažinti klaidų riziką, supaprastinti derinimą ir pagerinti bendrą kodo kokybę. Nors yra keletas kompromisų, kuriuos reikia apsvarstyti, nekintamumo privalumai dažnai nusveria kaštus, ypač sudėtinguose ir ilgalaikiuose projektuose. Tęsdami savo TypeScript kelionę, paverskite readonly tipus pagrindine savo kūrimo proceso dalimi, kad atskleistumėte visą nekintamumo potencialą ir kurtumėte tikrai patikimą programinę įrangą.