Türkçe

TypeScript'teki salt okunur tipler ile değişmez veri yapılarının gücünü ortaya çıkarın. İstenmeyen veri mutasyonlarını önleyerek daha öngörülebilir, sürdürülebilir ve sağlam uygulamalar oluşturmayı öğrenin.

TypeScript Salt Okunur (Readonly) Tipleri: Değişmez Veri Yapılarında Uzmanlaşma

Sürekli gelişen yazılım geliştirme dünyasında, sağlam, öngörülebilir ve sürdürülebilir kod arayışı bitmeyen bir çabadır. TypeScript, güçlü tipleme sistemiyle bu hedeflere ulaşmak için etkili araçlar sunar. Bu araçlar arasında, salt okunur (readonly) tipler, fonksiyonel programlamanın temel taşı ve daha güvenilir uygulamalar oluşturmanın anahtarı olan değişmezliği (immutability) zorunlu kılmak için kritik bir mekanizma olarak öne çıkar.

Değişmezlik (Immutability) Nedir ve Neden Önemlidir?

Değişmezlik, en temel anlamıyla, bir nesne oluşturulduktan sonra durumunun değiştirilememesi demektir. Bu basit konseptin kod kalitesi ve sürdürülebilirlik üzerinde derin etkileri vardır.

TypeScript'te Salt Okunur Tipler: Değişmezlik Cephaneliğiniz

TypeScript, readonly anahtar kelimesini kullanarak değişmezliği zorunlu kılmanın birkaç yolunu sunar. Şimdi farklı teknikleri ve bunların pratikte nasıl uygulanabileceğini inceleyelim.

1. Arayüzlerde ve Tiplerde Salt Okunur Özellikler

Bir özelliği salt okunur olarak bildirmenin en basit yolu, readonly anahtar kelimesini doğrudan bir arayüz veya tip tanımında kullanmaktır.


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

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

// person.id = "new-id"; // Hata: 'id' salt okunur bir özellik olduğu için atama yapılamaz.
person.name = "Bob"; // Buna izin verilir

Bu örnekte, id özelliği readonly olarak bildirilmiştir. TypeScript, nesne oluşturulduktan sonra onu değiştirmeye yönelik her türlü girişimi engelleyecektir. readonly değiştiricisine sahip olmayan name ve age özellikleri ise serbestçe değiştirilebilir.

2. Readonly Yardımcı Tipi

TypeScript, Readonly<T> adında güçlü bir yardımcı tip sunar. Bu jenerik tip, mevcut bir T tipini alır ve tüm özelliklerini readonly yaparak onu dönüştürür.


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

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

// point.x = 30; // Hata: 'x' salt okunur bir özellik olduğu için atama yapılamaz.

Readonly<Point> tipi, hem x hem de y'nin readonly olduğu yeni bir tip oluşturur. Bu, mevcut bir tipi hızlıca değişmez hale getirmenin kullanışlı bir yoludur.

3. Salt Okunur Diziler (ReadonlyArray<T>) ve readonly T[]

JavaScript'teki diziler doğası gereği değiştirilebilirdir. TypeScript, ReadonlyArray<T> tipini veya kısa yolu olan readonly T[]'yi kullanarak salt okunur diziler oluşturmanın bir yolunu sunar. Bu, dizinin içeriğinin değiştirilmesini engeller.


const numbers: ReadonlyArray<number> = [1, 2, 3, 4, 5];
// numbers.push(6); // Hata: 'push' özelliği 'readonly number[]' tipinde mevcut değil.
// numbers[0] = 10; // Hata: 'readonly number[]' tipindeki dizin imzası yalnızca okumaya izin verir.

const moreNumbers: readonly number[] = [6, 7, 8, 9, 10]; // ReadonlyArray ile eşdeğerdir
// moreNumbers.push(11); // Hata: 'push' özelliği 'readonly number[]' tipinde mevcut değil.

Diziyi değiştiren push, pop, splice gibi metotları kullanmaya veya doğrudan bir indekse atama yapmaya çalışmak, bir TypeScript hatasıyla sonuçlanacaktır.

4. const ve readonly Karşılaştırması: Farkı Anlamak

const ve readonly arasında ayrım yapmak önemlidir. const, değişkenin kendisine yeniden atama yapılmasını engellerken, readonly nesnenin özelliklerinin değiştirilmesini engeller. Farklı amaçlara hizmet ederler ve maksimum değişmezlik için birlikte kullanılabilirler.


const immutableNumber = 42;
// immutableNumber = 43; // Hata: 'immutableNumber' const değişkenine yeniden atama yapılamaz.

const mutableObject = { value: 10 };
mutableObject.value = 20; // Buna izin verilir çünkü *nesne* değil, sadece değişken const'tur.

const readonlyObject: Readonly<{ value: number }> = { value: 30 };
// readonlyObject.value = 40; // Hata: 'value' salt okunur bir özellik olduğu için atama yapılamaz.

const constReadonlyObject: Readonly<{ value: number }> = { value: 50 };
// constReadonlyObject = { value: 60 }; // Hata: 'constReadonlyObject' const değişkenine yeniden atama yapılamaz.
// constReadonlyObject.value = 60; // Hata: 'value' salt okunur bir özellik olduğu için atama yapılamaz.

Yukarıda gösterildiği gibi, const değişkenin her zaman bellekteki aynı nesneyi göstermesini sağlarken, readonly nesnenin iç durumunun değişmeden kalmasını garanti eder.

Pratik Örnekler: Salt Okunur Tipleri Gerçek Dünya Senaryolarında Uygulamak

Şimdi, salt okunur tiplerin çeşitli senaryolarda kod kalitesini ve sürdürülebilirliği artırmak için nasıl kullanılabileceğine dair bazı pratik örnekleri inceleyelim.

1. Yapılandırma Verilerini Yönetme

Yapılandırma verileri genellikle uygulamanın başlangıcında bir kez yüklenir ve çalışma zamanında değiştirilmemelidir. Salt okunur tipler kullanmak, bu verilerin tutarlı kalmasını sağlar ve kazara yapılan değişiklikleri önler.


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>) {
    // ... değişmeyeceklerini bilerek config.timeout ve config.apiUrl'i güvenle kullanın
}

fetchData("/data", config);

2. Redux Benzeri Durum Yönetimi Uygulamak

Redux gibi durum yönetimi kütüphanelerinde değişmezlik temel bir prensiptir. Salt okunur tipler, durumun (state) değişmez kalmasını ve reducer'ların mevcut durumu değiştirmek yerine yalnızca yeni durum nesneleri döndürmesini sağlamak için kullanılabilir.


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 }; // Yeni bir durum nesnesi döndür
    case "ADD_ITEM":
      return { ...state, items: [...state.items, action.payload] }; // Güncellenmiş öğelerle yeni bir durum nesnesi döndür
    default:
      return state;
  }
}

3. API Yanıtlarıyla Çalışmak

Bir API'den veri çekerken, özellikle UI bileşenlerini render etmek için kullanıyorsanız, yanıt verisini değişmez olarak kabul etmek genellikle arzu edilir. Salt okunur tipler, API verilerinin kazara mutasyona uğramasını önlemeye yardımcı olabilir.


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; // Hata: 'completed' salt okunur bir özellik olduğu için atama yapılamaz.
});

4. Coğrafi Verileri Modelleme (Uluslararası Örnek)

Coğrafi koordinatları temsil ettiğinizi düşünün. Bir koordinat bir kez ayarlandığında, ideal olarak sabit kalmalıdır. Bu, özellikle farklı coğrafi bölgelerde (örneğin, Kuzey Amerika, Avrupa ve Asya'yı kapsayan bir teslimat hizmeti için GPS koordinatları) çalışan haritalama veya navigasyon sistemleri gibi hassas uygulamalarla uğraşırken veri bütünlüğünü sağlar.


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 {
 // Enlem ve boylam kullanılarak yapılan karmaşık bir hesaplama düşünün
 // Basitlik için yer tutucu bir değer döndürülüyor
 return 1000; 
}

const distance = calculateDistance(tokyoCoordinates, newYorkCoordinates);
console.log("Tokyo ve New York arasındaki mesafe (yer tutucu):", distance);

// tokyoCoordinates.latitude = 36.0; // Hata: 'latitude' salt okunur bir özellik olduğu için atama yapılamaz.

Derinlemesine Salt Okunur Tipler: İç İçe Nesnelerle Başa Çıkma

Readonly<T> yardımcı tipi, yalnızca bir nesnenin doğrudan özelliklerini readonly yapar. Bir nesne iç içe nesneler veya diziler içeriyorsa, bu iç içe yapılar değiştirilebilir kalır. Gerçek anlamda derin değişmezlik elde etmek için, Readonly<T>'yi tüm iç içe geçmiş özelliklere özyinelemeli olarak uygulamanız gerekir.

İşte derinlemesine salt okunur bir tipin nasıl oluşturulacağına dair bir örnek:


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

Bu DeepReadonly<T> tipi, Readonly<T>'yi tüm iç içe geçmiş özelliklere özyinelemeli olarak uygular ve tüm nesne yapısının değişmez olmasını sağlar.

Dikkat Edilmesi Gerekenler ve Ödünleşimler

Değişmezlik önemli faydalar sunsa da, potansiyel ödünleşimlerin farkında olmak önemlidir.

Değişmez Veri Yapıları için Kütüphaneler

Birkaç kütüphane, TypeScript'te değişmez veri yapılarıyla çalışmayı basitleştirebilir:

Salt Okunur Tipleri Kullanmak için En İyi Uygulamalar

TypeScript projelerinizde salt okunur tiplerden etkili bir şekilde yararlanmak için şu en iyi uygulamaları izleyin:

Sonuç: TypeScript Salt Okunur Tipleriyle Değişmezliği Benimsemek

TypeScript'in salt okunur tipleri, daha öngörülebilir, sürdürülebilir ve sağlam uygulamalar oluşturmak için güçlü bir araçtır. Değişmezliği benimseyerek, hata riskini azaltabilir, hata ayıklamayı basitleştirebilir ve kodunuzun genel kalitesini artırabilirsiniz. Dikkate alınması gereken bazı ödünleşimler olsa da, değişmezliğin faydaları, özellikle karmaşık ve uzun ömürlü projelerde genellikle maliyetlerden daha ağır basar. TypeScript yolculuğunuza devam ederken, değişmezliğin tam potansiyelini ortaya çıkarmak ve gerçekten güvenilir bir yazılım oluşturmak için salt okunur tipleri geliştirme iş akışınızın merkezi bir parçası haline getirin.

TypeScript Salt Okunur (Readonly) Tipleri: Sağlam Uygulamalar için Değişmez Veri Yapılarında Uzmanlaşma | MLOG