İçeriğe geç
academia.sh

Ders 07 / 23

Arayüzler

Arayüzle yapısal sözleşme tanımı, seçmeli ve salt okunur alanlar, genişletme, indeks imzaları ve bildirim birleştirme.

İçindekiler

Önceki konu, tek tek değerlerin tiplerini kurdu. Bu konu, o tipleri adlandırmaya ve paylaşmaya geçer: bir modülün dışa açtığı sözleşme nasıl yazılır, aynı sözleşmeyi karşılayan farklı gerçekleştirimler nasıl tanımlanır?

Arayüz (interface), bir değerin taşıması gereken alanları ve yöntemleri adlandıran bildirimdir. Veri Yapıları kursunda tanıtılan soyut veri tipi düşüncesinin doğrudan karşılığıdır: yığıtın ne olduğu, iç gösterimiyle değil it, çek, tepe ve boş_mu işlemleriyle tanımlanır. Arayüz de bir tipi, iç yapısıyla değil sunduğu işlemlerle tanımlar.

Arayüz Bildirimi

Ölçüm kaydının modeli arayüz biçiminde yazılabilir:

type Birim = "C" | "Pa" | "%";

interface Olcum {
  readonly kimlik: string;
  sensor: string;
  deger: number;
  birim: Birim;
  zaman: number;
  not?: string;
}

const kayit: Olcum = {
  kimlik: "s-01",
  sensor: "sicaklik",
  deger: 21.4,
  birim: "C",
  zaman: 1706000000000,
};

console.log(kayit.not);
kayit.deger = 22.1;
kayit.kimlik = "s-02";
g1.ts(22,7): error TS2540: Cannot assign to 'kimlik' because it is a read-only property.

Bildirimde iki yeni işaret var.

readonly kimlik alanı, atamayı derleme anında engeller. Yirmi birinci satırdaki deger ataması geçerlidir, yirmi ikincisi değildir. Önceki derste kurulan sınır burada da geçerli: readonly çalışma zamanında silinir ve nesnenin kendisini korumaz.

not?: string alanı seçmeli (optional) bildirilmiştir. Nesne sabitinde bulunmamasına rağmen atama kabul edilir; kayit.not erişiminin tipi string | undefined olur ve doğrudan kullanılamadan önce daraltılması gerekir. Yirminci satır yalnızca yazdırdığı için sorun çıkmaz, çıktısı undefineddır.

Seçmeli alan, “bu bilgi bulunmayabilir” durumunu tip düzeyine taşır. Alternatifi — alanı zorunlu yapıp boş dizgi koymak — bilgiyi kaybeder: boş dizginin “ölçülmedi” mi yoksa “not boştu” mu anlamına geldiği belirsizdir.

Genişletme

Bir arayüz, başka bir arayüzün tüm gerekliliklerini devralabilir:

interface Olcum {
  kimlik: string;
  deger: number;
}

interface KonumluOlcum extends Olcum {
  enlem: number;
  boylam: number;
}

const kayit: KonumluOlcum = {
  kimlik: "s-01",
  deger: 21.4,
  enlem: 41.0,
  boylam: 29.0,
};
console.log(kayit.kimlik, kayit.enlem);

const eksik: KonumluOlcum = { kimlik: "s-02", deger: 22.1 };
g2.ts(19,7): error TS2739: Type '{ kimlik: string; deger: number; }' is missing the following properties from type 'KonumluOlcum': enlem, boylam

extends yazımı, kesişim tipiyle aynı sonucu üretir: KonumluOlcum tipi hem kendi alanlarını hem Olcumun alanlarını gerektirir. İki fark vardır.

Birincisi, extends uyumluluğu bildirim anında sınar:

interface Temel {
  birim: string;
}

interface Turemis extends Temel {
  birim: number;
}

declare const x: Turemis;
console.log(x.birim);
g6.ts(5,11): error TS2430: Interface 'Turemis' incorrectly extends interface 'Temel'.
  Types of property 'birim' are incompatible.
    Type 'number' is not assignable to type 'string'.

Tanı, çakışmanın bulunduğu bildirimi gösterir. Aynı çakışma kesişim tipiyle yazılsaydı, önceki derste görüldüğü gibi tip sessizce nevere indirgenir ve hata ancak kullanım yerinde ortaya çıkardı.

İkincisi, extends zinciri tanı iletilerinde ad olarak görünür ve okunabilirliği artırır.

Bu kalıtım, Programlama Temelleri kursundaki sınıf kalıtımından ayrıdır: burada devralınan şey davranış değil, yalnızca sözleşmedir. Arayüz gerçekleştirim içermez.

Bildirim Birleştirme

Arayüzlerin, tip takma adlarında bulunmayan bir özelliği vardır: aynı adla birden çok kez bildirilebilirler. Bildirimler birleştirilir.

interface Olcum {
  kimlik: string;
}

interface Olcum {
  deger: number;
}

const kayit: Olcum = { kimlik: "s-01", deger: 21.4 };
console.log(kayit.kimlik, kayit.deger);

const eksik: Olcum = { kimlik: "s-02" };
g3.ts(12,7): error TS2741: Property 'deger' is missing in type '{ kimlik: string; }' but required in type 'Olcum'.

İki bildirim tek bir arayüz oluşturmuştur; on ikinci satırın reddedilmesi bunun kanıtıdır.

Bu davranış bir kolaylık gibi görünse de asıl amacı farklıdır: var olan bir tipi dışarıdan genişletmeyi sağlar. Bir kütüphanenin tanımladığı arayüze, o kütüphaneyi değiştirmeden alan eklenebilir. Bildirim Dosyaları dersinde bu yeteneğin asıl kullanım yeri görülecektir.

Aynı yetenek bir risk de taşır: bir arayüzün tanımı tek bir dosyada tam olarak görülmeyebilir. Kendi kodunda tip birleştirmeye dayanmak, tanımın izini sürmeyi zorlaştırır.

İndeks İmzaları

Alan adlarının önceden bilinmediği durumlar için indeks imzası (index signature) yazılır:

interface EtiketliOlcum {
  kimlik: string;
  deger: number;
  [etiket: string]: string | number;
}

const kayit: EtiketliOlcum = {
  kimlik: "s-01",
  deger: 21.4,
  konum: "kazan-2",
  vardiya: 3,
};

console.log(kayit.konum, kayit["vardiya"]);
console.log(kayit.olmayanEtiket);

Bu dosya tip denetiminden geçer. Çıktısı:

kazan-2 3
undefined

İki kural işliyor. Birincisi, indeks imzası varken adı belirtilmiş alanların tipleri imzanın tipine uymak zorundadır; kimlik: string ve deger: number alanları string | number birleşiminin içinde kaldıkları için kabul edilir. Bir boolean alanı eklenseydi tanı gelirdi.

İkincisi ve daha önemlisi: indeks imzası, olmayan bir anahtara erişimi hata saymaz. Son satırdaki kayit.olmayanEtiket ifadesi derleyiciye göre string | number tipindedir, çalışma zamanında ise undefineddır. Tip sistemi burada gerçekliğin ötesinde bir söz verir.

Bu boşluğu kapatan bir derleyici seçeneği vardır — indeksli erişimlerin sonucuna undefined ekleyen bir katılık bayrağı — ve Derleyici Yapılandırması dersinde ele alınacaktır. Genel kural şudur: anahtar kümesi biliniyorsa indeks imzası yerine alanları tek tek yazmak ya da eşlenmiş tip kullanmak daha güvenlidir.

Sözleşme Olarak Arayüz

Arayüzün asıl gücü, veri biçimini tanımlamaktan çok davranış sözleşmesi kurmakta ortaya çıkar. Ölçüm kaydı modelinin bir sonraki adımı, kayıtların nereden geldiğini soyutlamaktır:

type Olcum = { kimlik: string; deger: number };

interface OlcumKaynagi {
  readonly ad: string;
  sonraki(): Olcum | null;
}

function diziKaynagi(ad: string, kayitlar: readonly Olcum[]): OlcumKaynagi {
  let sira = 0;
  return {
    ad,
    sonraki() {
      return sira < kayitlar.length ? kayitlar[sira++] : null;
    },
  };
}

function sabitKaynak(ad: string, deger: number): OlcumKaynagi {
  return {
    ad,
    sonraki() {
      return { kimlik: ad, deger };
    },
  };
}

function ilkIkisi(kaynak: OlcumKaynagi): string {
  const a = kaynak.sonraki();
  const b = kaynak.sonraki();
  return `${kaynak.ad}: ${a?.deger ?? "-"} ${b?.deger ?? "-"}`;
}

console.log(ilkIkisi(diziKaynagi("dosya", [{ kimlik: "s-01", deger: 21.4 }])));
console.log(ilkIkisi(sabitKaynak("sabit", 20)));

Çıktı:

dosya: 21.4 -
sabit: 20 20

ilkIkisi fonksiyonu iki kaynağın hiçbirini tanımaz; yalnızca OlcumKaynagi sözleşmesini bilir. Bu, Programlama Temelleri kursunda tanıtılan arayüz–gerçekleştirim ayrımının tip düzeyindeki hâlidir: sözleşme ile ona uyan kod ayrı ayrı değişebilir.

Dikkat edilmesi gereken bir nokta var: hiçbir gerçekleştirim OlcumKaynagi adını yazmıyor. diziKaynagi fonksiyonunun döndürdüğü nesne sabitinde “bu bir ölçüm kaynağıdır” diye bir bildirim yok; nesne yalnızca gerekli alanlara sahip olduğu için kabul ediliyor. Bu, TypeScript’in tip uyumluluğunun temelidir ve Yapısal Tip Uyumluluğu dersinde ayrıntılandırılacaktır.

Dönüş tipinin Olcum | null olması da bir tasarım kararıdır. ilkIkisi içinde a?.deger ve ?? "-" yazımları bu birleşimi ele almak zorundadır; derleyici, null olasılığının atlanmasına izin vermez.

Özet

  • Arayüz, bir değerin taşıması gereken alanları ve yöntemleri adlandırır; soyut veri tipi düşüncesinin tip düzeyindeki karşılığıdır.
  • readonly alanlar derleme anında atamayı engeller, seçmeli alanlar undefined olasılığını tipe taşır.
  • extends yazımı sözleşmeleri birleştirir ve çakışmayı bildirim anında sınar; gerçekleştirim devretmez.
  • Aynı adlı arayüz bildirimleri birleştirilir; bu yetenek dış tipleri genişletmek içindir ve kendi kodunda tanımın izini sürmeyi zorlaştırır.
  • İndeks imzası bilinmeyen anahtarları tipler ama olmayan anahtara erişimi hata saymaz; anahtar kümesi biliniyorsa alanlar tek tek yazılır.

Sonraki Adım

Aynı ölçüm modeli, interface yerine type anahtar sözcüğüyle de yazılabilirdi — nitekim önceki konuda öyle yazılmıştı. İki bildirim biçimi çoğu durumda birbirinin yerine geçer, ama tam olarak eşdeğer değildir. Sonraki ders farkları sıralayacak ve hangi durumda hangisinin seçileceğine dair bir ölçüt kuracak.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat