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.
readonlyalanlar derleme anında atamayı engeller, seçmeli alanlarundefinedolasılığını tipe taşır.extendsyazı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.