Bahasa Indonesia

Buka kekuatan struktur data imutabel di TypeScript dengan tipe readonly. Pelajari cara membuat aplikasi yang lebih prediktif, mudah dirawat, dan tangguh dengan mencegah mutasi data tak disengaja.

Tipe Readonly TypeScript: Menguasai Struktur Data Imutabel

Dalam lanskap pengembangan perangkat lunak yang terus berkembang, upaya untuk menciptakan kode yang tangguh, prediktif, dan mudah dirawat adalah sebuah usaha yang konstan. TypeScript, dengan sistem tipenya yang kuat, menyediakan alat yang ampuh untuk mencapai tujuan-tujuan ini. Di antara alat-alat ini, tipe readonly menonjol sebagai mekanisme krusial untuk menegakkan imutabilitas, sebuah landasan pemrograman fungsional dan kunci untuk membangun aplikasi yang lebih andal.

Apa itu Imutabilitas dan Mengapa Itu Penting?

Imutabilitas, pada intinya, berarti bahwa setelah sebuah objek dibuat, keadaannya tidak dapat diubah. Konsep sederhana ini memiliki implikasi yang mendalam bagi kualitas dan pemeliharaan kode.

Tipe Readonly di TypeScript: Senjata Imutabilitas Anda

TypeScript menyediakan beberapa cara untuk menerapkan imutabilitas menggunakan kata kunci readonly. Mari kita jelajahi berbagai teknik dan bagaimana cara menerapkannya dalam praktik.

1. Properti Readonly pada Interface dan Tipe

Cara paling mudah untuk mendeklarasikan sebuah properti sebagai readonly adalah dengan menggunakan kata kunci readonly secara langsung dalam definisi interface atau tipe.


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

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

// person.id = "new-id"; // Eror: Tidak dapat menugaskan nilai ke 'id' karena ini adalah properti read-only.
person.name = "Bob"; // Ini diizinkan

Dalam contoh ini, properti id dideklarasikan sebagai readonly. TypeScript akan mencegah setiap upaya untuk memodifikasinya setelah objek dibuat. Properti name dan age, yang tidak memiliki pengubah readonly, dapat dimodifikasi dengan bebas.

2. Tipe Utilitas Readonly

TypeScript menawarkan tipe utilitas yang kuat bernama Readonly<T>. Tipe generik ini mengambil tipe yang ada T dan mengubahnya dengan membuat semua propertinya menjadi readonly.


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

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

// point.x = 30; // Eror: Tidak dapat menugaskan nilai ke 'x' karena ini adalah properti read-only.

Tipe Readonly<Point> membuat tipe baru di mana x dan y keduanya bersifat readonly. Ini adalah cara yang nyaman untuk membuat tipe yang ada menjadi imutabel dengan cepat.

3. Array Readonly (ReadonlyArray<T>) dan readonly T[]

Array dalam JavaScript pada dasarnya bersifat mutabel. TypeScript menyediakan cara untuk membuat array readonly menggunakan tipe ReadonlyArray<T> atau singkatan readonly T[]. Ini mencegah modifikasi konten array.


const numbers: ReadonlyArray<number> = [1, 2, 3, 4, 5];
// numbers.push(6); // Eror: Properti 'push' tidak ada pada tipe 'readonly number[]'.
// numbers[0] = 10; // Eror: Tanda tangan indeks pada tipe 'readonly number[]' hanya mengizinkan pembacaan.

const moreNumbers: readonly number[] = [6, 7, 8, 9, 10]; // Setara dengan ReadonlyArray
// moreNumbers.push(11); // Eror: Properti 'push' tidak ada pada tipe 'readonly number[]'.

Mencoba menggunakan metode yang mengubah array, seperti push, pop, splice, atau menugaskan nilai langsung ke sebuah indeks, akan menghasilkan eror TypeScript.

4. const vs. readonly: Memahami Perbedaannya

Penting untuk membedakan antara const dan readonly. const mencegah penugasan ulang variabel itu sendiri, sementara readonly mencegah modifikasi properti objek. Keduanya melayani tujuan yang berbeda dan dapat digunakan bersama untuk imutabilitas maksimum.


const immutableNumber = 42;
// immutableNumber = 43; // Eror: Tidak dapat menugaskan kembali ke variabel const 'immutableNumber'.

const mutableObject = { value: 10 };
mutableObject.value = 20; // Ini diizinkan karena *objeknya* tidak const, hanya variabelnya.

const readonlyObject: Readonly<{ value: number }> = { value: 30 };
// readonlyObject.value = 40; // Eror: Tidak dapat menugaskan nilai ke 'value' karena ini adalah properti read-only.

const constReadonlyObject: Readonly<{ value: number }> = { value: 50 };
// constReadonlyObject = { value: 60 }; // Eror: Tidak dapat menugaskan kembali ke variabel const 'constReadonlyObject'.
// constReadonlyObject.value = 60; // Eror: Tidak dapat menugaskan nilai ke 'value' karena ini adalah properti read-only.

Seperti yang ditunjukkan di atas, const memastikan variabel selalu menunjuk ke objek yang sama di memori, sedangkan readonly menjamin bahwa keadaan internal objek tetap tidak berubah.

Contoh Praktis: Menerapkan Tipe Readonly dalam Skenario Dunia Nyata

Mari kita jelajahi beberapa contoh praktis bagaimana tipe readonly dapat digunakan untuk meningkatkan kualitas dan pemeliharaan kode dalam berbagai skenario.

1. Mengelola Data Konfigurasi

Data konfigurasi sering kali dimuat sekali saat aplikasi dimulai dan tidak boleh diubah selama runtime. Menggunakan tipe readonly memastikan bahwa data ini tetap konsisten dan mencegah modifikasi yang tidak disengaja.


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>) {
    // ... gunakan config.timeout dan config.apiUrl dengan aman, karena tahu nilainya tidak akan berubah
}

fetchData("/data", config);

2. Mengimplementasikan Manajemen State seperti Redux

Dalam pustaka manajemen state seperti Redux, imutabilitas adalah prinsip inti. Tipe readonly dapat digunakan untuk memastikan bahwa state tetap imutabel dan bahwa reducer hanya mengembalikan objek state baru alih-alih memodifikasi yang sudah ada.


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 }; // Mengembalikan objek state baru
    case "ADD_ITEM":
      return { ...state, items: [...state.items, action.payload] }; // Mengembalikan objek state baru dengan item yang diperbarui
    default:
      return state;
  }
}

3. Bekerja dengan Respons API

Saat mengambil data dari API, seringkali diinginkan untuk memperlakukan data respons sebagai imutabel, terutama jika Anda menggunakannya untuk merender komponen UI. Tipe readonly dapat membantu mencegah mutasi data API yang tidak disengaja.


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; // Eror: Tidak dapat menugaskan nilai ke 'completed' karena ini adalah properti read-only.
});

4. Memodelkan Data Geografis (Contoh Internasional)

Pertimbangkan untuk merepresentasikan koordinat geografis. Setelah sebuah koordinat diatur, idealnya harus tetap konstan. Ini memastikan integritas data, terutama ketika berurusan dengan aplikasi sensitif seperti sistem pemetaan atau navigasi yang beroperasi di berbagai wilayah geografis (misalnya, koordinat GPS untuk layanan pengiriman yang mencakup Amerika Utara, Eropa, dan 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 {
 // Bayangkan perhitungan kompleks menggunakan latitude dan longitude
 // Mengembalikan nilai placeholder untuk kesederhanaan
 return 1000; 
}

const distance = calculateDistance(tokyoCoordinates, newYorkCoordinates);
console.log("Jarak antara Tokyo dan New York (placeholder):", distance);

// tokyoCoordinates.latitude = 36.0; // Eror: Tidak dapat menugaskan nilai ke 'latitude' karena ini adalah properti read-only.

Tipe Readonly Mendalam: Menangani Objek Bersarang

Tipe utilitas Readonly<T> hanya membuat properti langsung dari sebuah objek menjadi readonly. Jika sebuah objek berisi objek atau array bersarang, struktur bersarang tersebut tetap mutabel. Untuk mencapai imutabilitas yang benar-benar mendalam, Anda perlu menerapkan Readonly<T> secara rekursif ke semua properti bersarang.

Berikut adalah contoh cara membuat tipe readonly yang mendalam:


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

Tipe DeepReadonly<T> ini secara rekursif menerapkan Readonly<T> ke semua properti bersarang, memastikan bahwa seluruh struktur objek bersifat imutabel.

Pertimbangan dan Timbal Balik

Meskipun imutabilitas menawarkan manfaat yang signifikan, penting untuk menyadari potensi timbal baliknya.

Pustaka untuk Struktur Data Imutabel

Beberapa pustaka dapat menyederhanakan pekerjaan dengan struktur data imutabel di TypeScript:

Praktik Terbaik Menggunakan Tipe Readonly

Untuk memanfaatkan tipe readonly secara efektif di proyek TypeScript Anda, ikuti praktik terbaik berikut:

Kesimpulan: Merangkul Imutabilitas dengan Tipe Readonly TypeScript

Tipe readonly TypeScript adalah alat yang ampuh untuk membangun aplikasi yang lebih prediktif, mudah dirawat, dan tangguh. Dengan merangkul imutabilitas, Anda dapat mengurangi risiko bug, menyederhanakan debugging, dan meningkatkan kualitas kode Anda secara keseluruhan. Meskipun ada beberapa timbal balik yang perlu dipertimbangkan, manfaat imutabilitas seringkali lebih besar daripada biayanya, terutama dalam proyek yang kompleks dan berumur panjang. Saat Anda melanjutkan perjalanan TypeScript Anda, jadikan tipe readonly sebagai bagian sentral dari alur kerja pengembangan Anda untuk membuka potensi penuh imutabilitas dan membangun perangkat lunak yang benar-benar andal.