Ders 12 / 23
Genellikler
Tip parametreleriyle biçimden bağımsız yapılar yazma, tip argümanı çıkarımı, kısıtlar, keyof kısıtı, varsayılan tip parametreleri ve genelliğin kötüye kullanımı.
İçindekiler
Önceki konudaki tiplerin tümü belirliydi: Olcum kayıtları tutan bir defter, Olcum alan
bir süzgeç. Oysa defterin yapısı kayıt tipinden bağımsızdır — aynı kod ölçümleri,
uyarıları veya olayları toplamalıdır.
Bunu sağlamanın iki kötü yolu vardır. Her kayıt tipi için ayrı bir defter yazmak kodu
çoğaltır; deftere any[] kullandırmak ise tip güvencesini kaldırır. Genellik
(generic), üçüncü yolu verir: yapıyı bir tip parametresiyle yazmak ve tipi kullanım
yerinde vermek.
Tip Parametreli Yapılar
Tip parametresi, bildirim adından sonra açılı ayraç içinde yazılır ve gövde boyunca bir tip gibi kullanılır:
interface Olcum { kimlik: string; deger: number; } class Defter<T> { private kayitlar: T[] = []; ekle(kayit: T): void { this.kayitlar.push(kayit); } sonuncu(): T | undefined { return this.kayitlar.at(-1); } get sayi(): number { return this.kayitlar.length; } } const olcumler = new Defter<Olcum>(); olcumler.ekle({ kimlik: "s-01", deger: 21.4 }); console.log(olcumler.sonuncu()?.deger, olcumler.sayi); const uyarilar = new Defter<string>(); uyarilar.ekle("sensor yanit vermiyor"); console.log(uyarilar.sonuncu()?.toUpperCase());
Çıktı:
21.4 1 SENSOR YANIT VERMIYOR
Tek bir sınıf iki farklı kayıt tipiyle kullanıldı ve her kullanımda tip bilgisi korundu:
olcumler.sonuncu() çağrısı Olcum | undefined, uyarilar.sonuncu() çağrısı
string | undefined verir. Dosyanın sonuna, bir boş satırdan sonra
olcumler.ekle("s-02"); satırı eklenirse:
m1.ts(30,15): error TS2345: Argument of type 'string' is not assignable to parameter of type 'Olcum'.
any[] çözümü bu tanıyı üretmezdi. Genellik, ortak yapıyı paylaşırken tip güvencesini
kaybetmemenin yoludur.
Dönüş tipinin T | undefined olması da tasarımın parçası: at(-1) çağrısı boş bir dizide
undefined verir ve tip bunu kaydeder. Çağıran, ?. yazımıyla bu olasılığı ele almak
zorundadır.
Tip Argümanı Çıkarımı
Tip argümanı çoğu zaman yazılmaz; derleyici argümanlardan çıkarır:
function ilk<T>(kayitlar: readonly T[]): T | undefined { return kayitlar[0]; } const sayi = ilk([21.4, 22.1]); const metin = ilk(["s-01", "s-02"]); console.log(sayi?.toFixed(1), metin?.toUpperCase());
Çıktı 21.4 S-01dir.
ilk<number> ve ilk<string> yazımlarına gerek kalmamıştır. sayi bildiriminin tipi
number | undefined, metin bildiriminin tipi string | undefined olarak çıkarılır ve
her birinde o tipin yöntemleri kullanılabilir.
Çıkarımın çalışması için tip parametresinin argümanlarda geçmesi gerekir. Yalnızca dönüş tipinde geçen bir tip parametresi çıkarılamaz; bu durumun tehlikesi dersin sonunda ele alınacaktır.
Kısıtlar
Tip parametresi hiçbir şey bilinmeyen bir tiptir; üzerinde işlem yapılamaz. Belirli üyelere erişmek için parametreye kısıt (constraint) yazılır:
interface Kimlikli { kimlik: string; } function kimlikleriTopla<T extends Kimlikli>(kayitlar: readonly T[]): string[] { return kayitlar.map((k) => k.kimlik); } console.log(kimlikleriTopla([{ kimlik: "s-01", deger: 21.4 }])); console.log(kimlikleriTopla([{ ad: "kazan-2" }]));
m4.ts(10,32): error TS2353: Object literal may only specify known properties, and 'ad' does not exist in type 'Kimlikli'.
T extends Kimlikli bildirimi iki iş yapar: gövdede k.kimlik erişimini olanaklı kılar
ve uymayan argümanları çağrı yerinde reddeder. Dokuzuncu satırın kabul edilmesi de önemli
— deger alanı fazlalıktır, kısıt yalnızca en az gerekliliği belirtir.
Kısıt yazmadan k.kimlik yazılsaydı derleyici erişimi reddederdi: T herhangi bir tip
olabilir, dizgi de olabilir, sayı da.
keyof ile Kısıt
Kısıtlar başka tip parametrelerine de başvurabilir. En kullanışlı biçimi, bir tipin alan
adlarını veren keyof işleciyle kurulur:
interface Olcum { kimlik: string; deger: number; birim: "C" | "Pa" | "%"; } function alan<T, A extends keyof T>(kayit: T, ad: A): T[A] { return kayit[ad]; } const kayit: Olcum = { kimlik: "s-01", deger: 21.4, birim: "C" }; const d: number = alan(kayit, "deger"); const b: "C" | "Pa" | "%" = alan(kayit, "birim"); console.log(d, b); alan(kayit, "konum");
m5.ts(16,13): error TS2345: Argument of type '"konum"' is not assignable to parameter of type 'keyof Olcum'.
Üç yeni yazım var. keyof T, T tipinin alan adlarının sabit tiplerinden oluşan bir
birleşimdir — burada "kimlik" | "deger" | "birim". A extends keyof T kısıtı, ikinci
argümanı bu adlarla sınırlar. T[A] ise indeksli erişim tipidir (indexed access
type): A adlı alanın tipini verir.
Sonuç, alan adına göre değişen bir dönüş tipidir. alan(kayit, "deger") çağrısının tipi
number, alan(kayit, "birim") çağrısının tipi "C" | "Pa" | "%"tir. Aşırı yükleme
yazmadan, alan sayısı kadar farklı imza elde edilmiştir.
Varsayılan Tip Parametresi
Tip parametrelerine varsayılan değer verilebilir. Sınırda doğrulamanın sonucunu taşıyan tip bunun tipik kullanımıdır:
type Sonuc<D, H = string> = | { durum: "basarili"; deger: D } | { durum: "hatali"; hata: H }; interface Olcum { kimlik: string; deger: number; } function oku(ham: string): Sonuc<Olcum> { const parcalar = ham.split("="); if (parcalar.length !== 2) { return { durum: "hatali", hata: `bicim gecersiz: ${ham}` }; } const deger = Number(parcalar[1]); if (!Number.isFinite(deger)) { return { durum: "hatali", hata: `sayi degil: ${parcalar[1]}` }; } return { durum: "basarili", deger: { kimlik: parcalar[0], deger } }; } function yaz(sonuc: Sonuc<Olcum>): string { return sonuc.durum === "basarili" ? `${sonuc.deger.kimlik}=${sonuc.deger.deger}` : `hata: ${sonuc.hata}`; } console.log(yaz(oku("s-01=21.4"))); console.log(yaz(oku("bozuk"))); console.log(yaz(oku("s-02=abc")));
Çıktı:
s-01=21.4 hata: bicim gecersiz: bozuk hata: sayi degil: abc
Sonuc<Olcum> yazımında ikinci tip argümanı verilmemiş, varsayılan string
kullanılmıştır. Hata tipinin ayrıntılanması gerektiğinde Sonuc<Olcum, HataKodu> yazılır
ve yaz fonksiyonu dışındaki kod değişmez.
Bu tip, ilk derste konan sınır kuralının olgunlaşmış hâlidir. oku fonksiyonu ne bir
istisna atar ne de null döndürür; başarısızlığı tipin bir üyesi olarak taşır ve
çağıranın onu ele almasını derleyici zorunlu kılar. Ayırt edici birleşim burada bir hata
yönetimi aracına dönüşmüştür.
Genelliğin Kötüye Kullanımı
Genellik her yerde iyileştirme sağlamaz. İki karşı örnek vardır.
Tek kez geçen tip parametresi. function yazdir<T>(deger: T): void imzasında T
yalnızca bir parametrede geçer ve hiçbir bağıntı kurmaz; unknown yazmak aynı işi daha
açık yapar. Genelliğin değeri, iki nokta arasında bir bağ kurmasındadır: girdi tipi
ile çıktı tipi, ya da iki parametre.
Yalnızca dönüş tipinde geçen tip parametresi. Bu, tip iddiasının gizlenmiş hâlidir:
function tehlikeli<T>(ham: unknown): T { return ham as T; } const kimlik = tehlikeli<string>(42); console.log(kimlik.toUpperCase());
Dosya tip denetiminden geçer, çalıştırıldığında ise:
TypeError: kimlik.toUpperCase is not a function
Çağıran T olarak ne yazarsa yazsın derleyici kabul eder, çünkü gövdedeki as T yazımı
denetimi susturur. Bu imza, bir doğrulama vaat ediyor gibi görünüp hiçbir şey
doğrulamaz — any döndürmekten daha tehlikelidir, çünkü tip güvenli görünür.
Kural: tip parametresi en az iki yerde geçmelidir. Yalnızca dönüş tipinde geçiyorsa,
imza unknown döndürmeli ve çağıran daraltmayı kendisi yapmalıdır.
Özet
- Tip parametresi, bir yapının biçimini kayıt tipinden bağımsız yazmayı sağlar ve
anyçözümünün aksine tip güvencesini korur. - Tip argümanları çoğu zaman çağrı argümanlarından çıkarılır; çıkarım için parametrenin argüman listesinde geçmesi gerekir.
- Kısıt (
T extends K), tip parametresi üzerinde işlem yapılabilmesini sağlar ve uymayan argümanları çağrı yerinde reddeder. keyof Talan adlarının birleşimini,T[A]indeksli erişim tipini verir; ikisi birlikte alan adına göre değişen dönüş tipleri kurar.- Varsayılan tip parametresi yaygın durumu kısaltır; ayırt edici birleşimle kurulan sonuç tipi, başarısızlığı tipin bir üyesi olarak taşır.
- Tip parametresi en az iki yerde geçmelidir; yalnızca dönüş tipinde geçen bir parametre, gizlenmiş bir tip iddiasıdır.
Sonraki Adım
Bu derste birkaç kez bir birleşim tipinin üzerinde sınama yapıldı ve derleyici tipi tek bir üyeye indirdi. Bu indirgemenin kuralları henüz sıralanmadı: hangi sınamalar kanıt sayılır, kanıt ne kadar uzağa taşınır, kendi yazdığınız doğrulama fonksiyonunun sonucu derleyiciye nasıl bildirilir? Sonraki ders daraltmayı ayrıntılı ele alacak.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.