Ders 04 / 23
Tip Çıkarımı
Derleyicinin ilk değerden, bağlamdan ve dönüş ifadelerinden tip çıkarma kuralları; genişletme davranışı ve tip açıklamasının hangi noktalarda gerekli olduğu.
İçindekiler
Önceki derslerin örneklerinde derleyici, yazılmamış birçok tipi biliyordu: daraltma
sonrası alanların tipleri, dizi elemanlarının tipi, reduce geri çağrısının parametreleri.
Bu bilgiyi nereden aldı?
Yanıt tip çıkarımıdır (type inference): derleyici, bir ifadenin tipini çevresindeki bilgiden hesaplar. Çıkarım TypeScript’i kullanılabilir kılan şeydir — her değişkene tip yazmak gerekseydi tip katmanının maliyeti yararını aşardı. Bu ders çıkarımın kurallarını, sınırlarını ve tip açıklamasının hâlâ gerektiği noktaları ele alır.
Çıkarılan Tipi Görmek
Çıkarımı incelemenin en doğrudan yolu, derleyiciden bildirim dosyası üretmesini istemektir. Bildirim dosyası, bir modülün dışa aktardığı adların tiplerini yazar; çıkarılan tipler orada açıkça görünür.
export const birim = "C"; export let degisken = "C"; export const deger = 21.4; export const olcum = { kimlik: "s-01", deger: 21.4, birim: "C" }; export const degerler = [21.4, 22.1]; export const karisik = [21.4, "yok"]; export function ortalama(olcumler: number[]) { return olcumler.reduce((t, d) => t + d, 0) / olcumler.length; }
Dosya cikarim.ts adıyla kaydedilip
tsc --strict --target es2022 --declaration --emitDeclarationOnly cikarim.ts komutuyla
derlendiğinde yanına cikarim.d.ts yazılır:
export declare const birim = "C"; export declare let degisken: string; export declare const deger = 21.4; export declare const olcum: { kimlik: string; deger: number; birim: string; }; export declare const degerler: number[]; export declare const karisik: (string | number)[]; export declare function ortalama(olcumler: number[]): number;
Bu dosya, çıkarımın altı kuralını tek bakışta gösterir. Sırayla ele alalım.
İlk Değerden Çıkarım ve Genişletme
birim sabitinin tipi string değil "C"dir — yalnızca tek bir değer içeren küme. deger
sabitinin tipi de number değil 21.4tür. Buna karşılık degisken bildiriminin tipi
stringe genişletilmiştir (widening).
Kural şudur: değeri değişebilen bir bildirimde sabit tip tutulamaz, çünkü bildirim daha
sonra aynı tipte başka bir değer alabilir. let degisken = "C" bildiriminden sonra
degisken = "Pa" yazılabilir; tip "C" kalsaydı bu atama reddedilirdi. Değeri
değişemeyen bir bildirimde ise genişletmeye gerek yoktur.
Aynı gerekçe nesne alanları için de geçerlidir. olcum sabiti const ile bildirilmiş
olmasına rağmen birim alanının tipi "C" değil string çıkarılmıştır. Nedeni,
JavaScript Temelleri kursunda kurulan ayrımdır: const bildirimi bağlamayı sabitler,
nesnenin içeriğini değil. olcum.birim = "Pa" yazımı geçerli bir JavaScript ifadesidir,
dolayısıyla alanın tipi genişletilir.
Bu davranışın doğrudan bir sonucu vardır:
type Birim = "C" | "Pa" | "%"; type Olcum = { kimlik: string; deger: number; birim: Birim }; const ham = { kimlik: "s-01", deger: 21.4, birim: "C" }; const kayit: Olcum = ham; console.log(kayit);
d4.ts(5,7): error TS2322: Type '{ kimlik: string; deger: number; birim: string; }' is not assignable to type 'Olcum'.
Types of property 'birim' are incompatible.
Type 'string' is not assignable to type 'Birim'.
Atama başarısız olur, çünkü ham.birim alanının tipi stringe genişlemiştir ve string,
üç değerli Birim kümesine sığmaz. Genişletmeyi engellemenin yolu Sabit Tipler ve
as const dersinin konusudur.
Dizilerde çıkarım, elemanların tiplerinin birleşimini alır: degerler için number[],
karışık elemanlı karisik için (string | number)[]. Dizi de değiştirilebilir olduğu
için eleman tipleri genişletilir.
Bağlamsal Tipleme
Çıkarım tek yönlü değildir. Bir ifadenin tipi, o ifadenin beklendiği yerden de gelebilir. Buna bağlamsal tipleme (contextual typing) denir:
const degerler: number[] = [21.4, 22.1, 23.0]; degerler.forEach((d) => console.log(d.toFixed(1))); degerler.forEach((d) => console.log(d.toUpperCase()));
d1.ts(4,39): error TS2339: Property 'toUpperCase' does not exist on type 'number'.
d parametresine tip yazılmamıştır. Derleyici tipi, forEach yönteminin beklediği geri
çağrı tipinden alır: degerler bir number[] olduğuna göre geri çağrının ilk parametresi
numberdır. Üçüncü satır geçerlidir, dördüncü satır dizgi yöntemi çağırdığı için
reddedilir.
Bağlamsal tipleme, örtük any denetiminin neden geri çağrılarda tetiklenmediğini de
açıklar: parametre tipsiz görünse de bağlamdan gelen bir tipi vardır.
Dönüş Tipi Çıkarımı ve Hatanın Yeri
Fonksiyon dönüş tipleri de çıkarılır. Yukarıdaki bildirim dosyasında ortalama
fonksiyonunun dönüş tipi yazılmadığı hâlde number olarak görünüyor.
Çıkarımın rahatlığının bir bedeli vardır ve bu bedel, hatanın nerede bildirildiğinde ortaya çıkar. Önce dönüş tipi yazılmamış hâli:
function olcumOlustur(kimlik: string, ham: string) { return { kimlik, deger: ham }; } const kayit = olcumOlustur("s-01", "21.4"); const yuvarlanmis: string = kayit.deger.toFixed(1); console.log(yuvarlanmis);
d2.ts(6,41): error TS2551: Property 'toFixed' does not exist on type 'string'. Did you mean 'fixed'?
Hata altıncı satırda, yani fonksiyonun kullanıldığı yerde bildirilir. Oysa yanlış olan
şey ikinci satırdadır: deger alanına dizgi konmuştur. Aynı fonksiyon, modelin tipiyle
birlikte yazıldığında:
type Olcum = { kimlik: string; deger: number }; function olcumOlustur(kimlik: string, ham: string): Olcum { return { kimlik, deger: ham }; } console.log(olcumOlustur("s-01", "21.4"));
d3.ts(4,20): error TS2322: Type 'string' is not assignable to type 'number'.
Tanı artık dördüncü satırda — hatanın kaynağında. Fark, tek bir fonksiyonda önemsiz görünür; onlarca modülden geçen bir çağrı zincirinde ise hatanın kaynağını bulma süresini belirler.
Buradan pratik bir kural çıkar: dışa açılan sınırlarda tip yazılır, iç ayrıntılarda çıkarıma bırakılır. Bir modülün dışa aktardığı fonksiyonların parametre ve dönüş tipleri, bir sınıfın genel yöntemleri ve modülün veri tipleri elle yazılır. Yerel değişkenler, geri çağrı parametreleri ve özel yardımcı fonksiyonlar çıkarıma bırakılır.
Bu kuralın ikinci gerekçesi, tip açıklamasının bir sözleşme olmasıdır. Yazılmış bir dönüş tipi, gövde değiştiğinde sözleşmenin bozulup bozulmadığını sınar. Çıkarılan dönüş tipi ise gövdeyle birlikte sessizce değişir — ve sözleşmenin değiştiği, ancak kullanan kodda bir hata belirdiğinde fark edilir.
Çıkarımın Sınırları
Çıkarım her boşluğu dolduramaz. Üç tipik durum vardır.
Boş kapla başlayan birikeç. Yerel bir const gecerliler = [] bildiriminde derleyici
eleman tipini sonraki eklemelerden toparlar. Bu, hatanın yine uzağa taşınması demektir:
type Olcum = { kimlik: string; deger: number }; function suz(kayitlar: Olcum[]): Olcum[] { const gecerliler = []; for (const k of kayitlar) { if (k.deger > 0) { gecerliler.push(k.kimlik); } } return gecerliler; } console.log(suz([{ kimlik: "s-01", deger: 21.4 }]));
bos2.ts(10,3): error TS2322: Type 'string[]' is not assignable to type 'Olcum[]'. Type 'string' is not assignable to type 'Olcum'.
Yanlış olan yedinci satırdır — kaydın kendisi yerine kimliği eklenmiştir — ama tanı
onuncu satırda çıkar. const gecerliler: Olcum[] = [] yazılsaydı tanı eklemenin yapıldığı
satırda görünürdü.
Dışarıdan gelen veri. JSON.parse çağrısının dönüş tipi anydir; çıkarımın
kullanacağı bir bilgi yoktur. Önceki derste kurulan kural burada geçerlidir — dönüş
unknown olarak alınır ve sınanır.
Aşırı geniş çıkarım. Bir nesne sabitinden çıkarılan tip genellikle amaçlanandan
gevşektir; yukarıdaki birim: string örneği bunun tipik hâlidir. Amaçlanan tip
yazıldığında derleyici hem daraltır hem de sabitin o tipe uyduğunu sınar.
Özet
- Tip çıkarımı, bir ifadenin tipini ilk değerinden, bağlamından ve dönüş ifadelerinden hesaplar; çıkarılan tipler bildirim dosyası üretilerek görüntülenebilir.
- Değeri değişebilen bildirimlerde sabit tipler genişletilir;
constbildirimi bağlamayı sabitlediği için sabit tip korunur, ancak nesne alanları yine genişletilir. - Bağlamsal tipleme, bir ifadenin tipini beklendiği yerden alır; geri çağrı parametreleri bu yolla tiplenir.
- Dönüş tipi yazılmadığında hata, fonksiyonun kullanıldığı yerde bildirilir; yazıldığında kaynağında bildirilir.
- Dışa açılan sınırlarda tip yazılır, iç ayrıntılar çıkarıma bırakılır; tip açıklaması aynı zamanda gövde değiştiğinde sınanan bir sözleşmedir.
Sonraki Adım
Ölçüm kaydının birim alanı üç değerden birini alabiliyordu ve bu, birbirinin alternatifi
olan tiplerin birleştirilmesiyle yazılmıştı. Aynı işlemin bir de kesişim yönü vardır: iki
tipin gerekliliklerini aynı anda taşıyan değerler. Sonraki ders, birleşim ve kesişim
tiplerinin kurallarını, birleşim üzerinde hangi işlemlerin yapılabildiğini ve kesişimin ne
zaman boşa düştüğünü ele alacak.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.