Ders 09 / 23
Sınıflar ve Erişim Belirteçleri
Sınıf alanlarının tiplenmesi, görünürlük belirteçlerinin derleme anındaki ve çalışma zamanındaki karşılıkları, parametre özellikleri, soyut sınıflar ve implements bildirimi.
İçindekiler
Buraya kadarki sözleşmeler yalnızca veriyi ve işlem imzalarını tanımladı; gerçekleştirim her seferinde ayrı bir fonksiyondan geldi. Sınıflar bu ikisini bir arada tutar.
JavaScript’te Nesneler ve Fonksiyonlar kursunda sınıf sözdiziminin prototip modeline karşılığı kurulmuştu. TypeScript bu sözdizimine üç şey ekler: alanların ve yöntemlerin tipleri, görünürlük belirteçleri (access modifier) ve soyut bildirimler. Bu eklerin bazıları silinir, bazıları kod üretir; ayrım bu dersin merkezidir.
Alanların Tiplenmesi ve Görünürlük
Ölçüm kayıtlarını toplayan bir defter sınıfı, üç farklı görünürlük biçimini bir arada gösterir:
interface Olcum { readonly kimlik: string; deger: number; } class OlcumDefteri { private kayitlar: Olcum[] = []; #gizliSayac = 0; constructor(readonly ad: string) {} ekle(olcum: Olcum): void { this.kayitlar.push(olcum); this.#gizliSayac += 1; } get sayi(): number { return this.kayitlar.length; } } const defter = new OlcumDefteri("kazan-2"); defter.ekle({ kimlik: "s-01", deger: 21.4 }); console.log(defter.ad, defter.sayi);
Dosya defter.ts adıyla tsc --strict --target es2022 defter.ts komutuyla derlendiğinde
üretilen JavaScript:
"use strict"; class OlcumDefteri { ad; kayitlar = []; #gizliSayac = 0; constructor(ad) { this.ad = ad; } ekle(olcum) { this.kayitlar.push(olcum); this.#gizliSayac += 1; } get sayi() { return this.kayitlar.length; } } const defter = new OlcumDefteri("kazan-2"); defter.ekle({ kimlik: "s-01", deger: 21.4 }); console.log(defter.ad, defter.sayi);
node defter.js çıktısı kazan-2 1dir. Çıktıda üç şey görülür.
private silinmiştir. private kayitlar alanı, derlenmiş kodda kayitlar olarak
durur ve dışarıdan okunabilir. #gizliSayac durmaktadır. Bu, TypeScript’e değil
JavaScript’e ait bir özelliktir ve çalışma zamanında zorunlu tutulur.
constructor(readonly ad: string) yazımı kod üretmiştir. Buna parametre özelliği
(parameter property) denir: yapıcı parametresine bir görünürlük veya readonly belirteci
konduğunda derleyici, aynı adlı bir alan bildirir ve atamayı ekler. Numaralandırma gibi,
bu da tip silme ilkesinin istisnasıdır.
Farkın sonucu, derlenmiş sınıf doğrudan JavaScript’ten kullanıldığında gözlenir. Aşağıdaki dosya, yukarıda üretilen sınıf gövdesini alıp üç sorgu ekler:
class OlcumDefteri { ad; kayitlar = []; #gizliSayac = 0; constructor(ad) { this.ad = ad; } ekle(olcum) { this.kayitlar.push(olcum); this.#gizliSayac += 1; } get sayi() { return this.kayitlar.length; } } const defter = new OlcumDefteri("kazan-2"); defter.ekle({ kimlik: "s-01", deger: 21.4 }); console.log(Object.keys(defter)); console.log(defter.kayitlar); console.log(defter.gizliSayac);
[ 'ad', 'kayitlar' ]
[ { kimlik: 's-01', deger: 21.4 } ]
undefined
kayitlar alanı hem sayılabilir hem okunabilir; #gizliSayac ise gizliSayac adıyla
erişilmeye çalışıldığında undefined verir, çünkü öyle bir genel alan yoktur.
Derleme anında ikisi de korunur, ama farklı tanılarla:
class OlcumDefteri { private kayitlar: number[] = []; #sayac = 0; ekle(deger: number): void { this.kayitlar.push(deger); this.#sayac += 1; } } const defter = new OlcumDefteri(); defter.ekle(21.4); console.log(defter.kayitlar); console.log(defter.#sayac);
i1.ts(13,20): error TS2341: Property 'kayitlar' is private and only accessible within class 'OlcumDefteri'. i1.ts(14,20): error TS18013: Property '#sayac' is not accessible outside class 'OlcumDefteri' because it has a private identifier.
Seçim ölçütü şudur: görünürlük bir tasarım kuralıysa private, bir güvenlik sınırıysa
#. Ekip içinde yanlış kullanımı önlemek için private yeterlidir ve derleme anında
yakalar. Kütüphane sınırında, denetlenmeyen kodun erişmemesi gerekiyorsa # kullanılır.
protected belirteci de yalnızca derleme anında geçerlidir ve alanı sınıfın kendisine ve
alt sınıflarına açar.
Soyut Sınıflar
Soyut sınıf (abstract class), gerçekleştirilmemiş üyeler bildirebilen ve doğrudan örneklenemeyen sınıftır. Ölçüm kaynağı sözleşmesi, ortak bir davranış da taşıyacaksa arayüz yerine soyut sınıfla yazılır:
interface Olcum { kimlik: string; deger: number; } abstract class OlcumKaynagi { abstract readonly ad: string; protected abstract sonraki(): Olcum | null; tumu(): Olcum[] { const toplanan: Olcum[] = []; let kayit = this.sonraki(); while (kayit !== null) { toplanan.push(kayit); kayit = this.sonraki(); } return toplanan; } } class DiziKaynagi extends OlcumKaynagi { readonly ad = "dizi"; private sira = 0; constructor(private readonly kayitlar: readonly Olcum[]) { super(); } protected sonraki(): Olcum | null { return this.sira < this.kayitlar.length ? this.kayitlar[this.sira++] : null; } } const kaynak = new DiziKaynagi([ { kimlik: "s-01", deger: 21.4 }, { kimlik: "s-02", deger: 22.1 }, ]); console.log(kaynak.ad, kaynak.tumu().length);
Çıktı dizi 2dir.
Yapı, iki tür üyeyi ayırır. sonraki yöntemi soyuttur: her kaynak farklı gerçekleştirir.
tumu yöntemi ise somuttur ve sonraki üzerine kurulmuş ortak davranışı taşır. Alt sınıf
yalnızca değişen parçayı yazar.
Dosyanın sonuna, bir boş satırdan sonra const olmaz = new OlcumKaynagi(); satırı
eklenirse derleyici şu tanıyı verir:
i2.ts(40,15): error TS2511: Cannot create an instance of an abstract class.
abstract belirteci de silinir — derlenmiş çıktıda sıradan bir sınıf kalır ve JavaScript
tarafından örneklenebilir. Yasak, derleme anına aittir.
implements
Bir sınıfın belirli bir sözleşmeyi karşıladığı implements ile bildirilir:
interface OlcumKaynagi { readonly ad: string; sonraki(): number | null; } class BosKaynak implements OlcumKaynagi { readonly ad = "bos"; }
i4.ts(6,7): error TS2420: Class 'BosKaynak' incorrectly implements interface 'OlcumKaynagi'. Property 'sonraki' is missing in type 'BosKaynak' but required in type 'OlcumKaynagi'.
implements bir sınama bildirimidir, bir kalıtım değil. Sınıfa hiçbir üye eklemez;
yalnızca sözleşmeye uygunluğu denetler ve tanıyı sınıfın bildirim satırında verir. Bu
bildirim olmasaydı sınıf yine derlenir, uyumsuzluk ancak kullanım yerinde ortaya çıkardı.
implements yazımı tümüyle silinir; derlenmiş çıktıda izi kalmaz.
Soyut Sınıf mı, Arayüz mü
İki araç örtüşür; ölçüt şudur:
| Ölçüt | Arayüz | Soyut sınıf |
|---|---|---|
| Ortak gerçekleştirim taşır | Hayır | Evet |
| Çalışma zamanı kodu üretir | Hayır | Evet |
| Bir tip birden çoğunu karşılar | Evet | Hayır (tek üst sınıf) |
| Durum (alan değeri) taşır | Hayır | Evet |
Yalnızca sözleşme gerekiyorsa arayüz. Silinir, birden çoğu birlikte kullanılabilir ve gerçekleştirimi bağlamaz.
Sözleşmeyle birlikte ortak davranış gerekiyorsa soyut sınıf. Yukarıdaki tumu yöntemi
bunun örneğidir: tek yerde yazılmış, tüm kaynaklarda geçerli bir algoritma.
Programlama Temelleri kursunda tanıtılan sınıf değişmezi kavramı burada tip düzeyinde
destek bulur: private alanlar üzerinde tutulan bir değişmez, dışarıdan bozulamaz — çünkü
alanlara yalnızca sınıfın kendi yöntemleri erişebilir ve derleyici bunu sınar.
Özet
- Sınıf alanlarının ve yöntemlerinin tipleri silinir;
privateveprotectedyalnızca derleme anında geçerlidir,#ile bildirilen alanlar çalışma zamanında da korunur. - Parametre özelliği (
constructor(readonly ad: string)) kod üretir: alan bildirimi ve atama derleyici tarafından eklenir. - Soyut sınıf, gerçekleştirilmemiş üyeler bildirir ve doğrudan örneklenemez;
abstractbelirteci derlenmiş çıktıda kalmaz. implementsbir sınama bildirimidir; sınıfa üye eklemez, tanıyı bildirim satırında verir ve tümüyle silinir.- Yalnızca sözleşme için arayüz, sözleşmeyle birlikte ortak davranış için soyut sınıf seçilir.
Sonraki Adım
Sınıf yöntemleri ve tek başına duran fonksiyonlar, bu derste yalnızca kullanıldı;
tiplerinin nasıl yazıldığı ele alınmadı. Bir fonksiyonun tipi parametre ve dönüş
tiplerinden ibaret değildir: seçmeli parametreler, kalan parametreler, aşırı yükleme ve
this bağlamı da tipin parçasıdır. Sonraki ders fonksiyon tiplerini ayrıntılı kuracak.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.