Ders 11 / 23
Yapısal Tip Uyumluluğu
Ördek tiplemesinin kuralları, nesne sabitlerinde fazlalık özellik denetimi, fonksiyon parametrelerinde yön kuralı ve yapısal uyumluluğu kırmanın yolları.
İçindekiler
Bu konudaki örneklerin hiçbirinde bir değer, uyduğu tipin adını yazmadı. Nesne sabitleri arayüzlere, fonksiyonlar fonksiyon tiplerine yalnızca biçimleri uyduğu için kabul edildi.
Bu, TypeScript’in tip uyumluluğunun temel kuralıdır. Yapısal tipleme (structural typing) — halk arasındaki adıyla ördek tiplemesi (duck typing) — bir değerin bir tipe uyup uymadığını, bildirdiği ada değil taşıdığı üyelere bakarak belirler. Karşıtı adsal tiplemedir (nominal typing): tip uyumu ancak açıkça bildirildiğinde geçerlidir.
Bu ders yapısal tiplemenin kurallarını, çıkardığı sorunları ve gerektiğinde nasıl kırılacağını ele alır.
Temel Kural
Bir tip, hedef tipin gerektirdiği tüm üyeleri taşıyorsa uyumludur. Fazladan üye taşıması uyumu bozmaz:
interface Olcum { kimlik: string; deger: number; } const uyusan = { kimlik: "s-01", deger: 21.4, konum: "kazan-2" }; const dolayli: Olcum = uyusan; console.log(dolayli.kimlik); const dogrudan: Olcum = { kimlik: "s-02", deger: 22.1, konum: "kazan-3" };
k1.ts(10,56): error TS2353: Object literal may only specify known properties, and 'konum' does not exist in type 'Olcum'.
Yedinci satır geçerlidir: uyusan nesnesi Olcumun istediği iki alanı taşır, üçüncü alan
fazlalıktır ve yok sayılır. Onuncu satır ise aynı biçimdeki bir nesneyi doğrudan
yazdığında reddedilir.
Fark, fazlalık özellik denetimidir (excess property check). Derleyici, doğrudan bir tipe atanan taze nesne sabitlerinde bilinmeyen alanları hata sayar. Gerekçe pratiktir: doğrudan yazılan bir sabitteki fazladan alan neredeyse her zaman yazım hatasıdır ya da yanlış anlaşılmış bir sözleşmedir. Değişken üzerinden gelen bir nesnede ise fazladan alan olağandır — nesne başka bir amaç için de kullanılıyor olabilir.
Denetimin dar kapsamı bilinçlidir ve tam bir koruma sağlamaz. Bir ara değişkene atamak denetimi atlatır; bu, kuralın kaçamağı değil kapsamıdır.
Adın Rolü Yok
Yapısal tiplemenin en dikkat çekici sonucu, birbiriyle ilgisiz iki tipin uyumlu olabilmesidir:
interface Olcum { kimlik: string; deger: number; } interface Urun { kimlik: string; deger: number; } const olcum: Olcum = { kimlik: "s-01", deger: 21.4 }; const urun: Urun = olcum; console.log(urun.deger);
Bu dosya hatasız derlenir. Olcum ve Urun farklı kavramlardır, deger alanları farklı
şeyler ölçer; tip sistemine göre aynı tiptir.
Bu davranış bir kusur değil, tasarım kararıdır. TypeScript var olan JavaScript’in üzerine eklenir; JavaScript’te nesneler bildirilmiş tiplerle değil sahip oldukları özelliklerle dolaşır. Bir kütüphaneye “şu alanları taşıyan bir nesne” verilebiliyorsa, tip sisteminin de aynı esnekliği tanıması gerekir. Yapısal tipleme, önceki derste arayüzü hiçbir sınıfın adını yazmadan gerçekleştirebilmenin de nedenidir.
Bedeli, anlamı aynı olmayan tiplerin karışabilmesidir. Bu kursun sonunda buna karşı bir araç kurulacaktır.
Fonksiyonlarda Yön Kuralı
Fonksiyon tiplerinde uyumluluk, parametreler için ters yönde çalışır:
interface Olcum { kimlik: string; deger: number; } interface KonumluOlcum extends Olcum { konum: string; } type Isleyici = (olcum: Olcum) => void; const konumaGore = (olcum: KonumluOlcum): void => console.log(olcum.konum); const isleyici: Isleyici = konumaGore; isleyici({ kimlik: "s-01", deger: 21.4 });
k4.ts(13,7): error TS2322: Type '(olcum: KonumluOlcum) => void' is not assignable to type 'Isleyici'.
Types of parameters 'olcum' and 'olcum' are incompatible.
Property 'konum' is missing in type 'Olcum' but required in type 'KonumluOlcum'.
Gerekçe on beşinci satırda görülür: isleyici tipi, Olcum alan bir fonksiyon vaat eder;
çağıran konum alanı olmayan bir kayıt gönderir; konumaGore ise o alana erişir. Atama
kabul edilseydi program çalışma zamanında undefined üzerinde işlem yapardı.
Kural şu biçimde özetlenir: bir fonksiyon, daha geniş parametre kabul eden bir tipe atanabilir; daha dar parametre isteyen bir fonksiyon atanamaz. Dönüş tipinde ise yön düzdür — daha dar bir dönüş tipi, daha geniş bir dönüş tipi bekleyen yere uyar.
Bu kural yalnızca ok yazımıyla bildirilen fonksiyon tiplerinde uygulanır. Yöntem sözdizimiyle bildirilen üyelerde derleyici her iki yöne de izin verir:
interface Olcum { kimlik: string; deger: number; } interface KonumluOlcum extends Olcum { konum: string; } interface IsleyiciYontem { isle(olcum: Olcum): void; } const nesne: IsleyiciYontem = { isle(olcum: KonumluOlcum): void { console.log(olcum.konum); }, }; nesne.isle({ kimlik: "s-01", deger: 21.4 });
Bu dosya tip denetiminden geçer ve çalıştırıldığında undefined yazar.
Bu, tip sisteminin bilinçli olarak bıraktığı bir boşluktur. Nedeni dizi yöntemlerinin
tiplenmesidir: Array<T> üzerindeki yöntemler yöntem sözdizimiyle yazılmıştır ve katı
kural uygulansaydı sık kullanılan pek çok kalıp reddedilirdi. İlk derste sözü edilen “tam
güvenilir olmama” durumunun somut bir örneğidir.
Pratik sonuç: geri çağrı tiplerini ok yazımıyla bildirmek daha güvenlidir.
Yapısal Uyumluluğu Kırmak
İki nedenle uyumluluğun kırılması istenebilir: farklı anlam taşıyan aynı biçimli tipleri ayırmak ve bir değerin belirli bir doğrulamadan geçtiğini tip düzeyinde kaydetmek.
Sınıflarda private veya protected üye bulunması uyumluluğu kendiliğinden kırar:
class OlcumDefteri { private kayitlar: number[] = []; } class UrunDefteri { private kayitlar: number[] = []; } const defter: OlcumDefteri = new UrunDefteri(); console.log(defter);
k3.ts(9,7): error TS2322: Type 'UrunDefteri' is not assignable to type 'OlcumDefteri'. Types have separate declarations of a private property 'kayitlar'.
İki alan aynı adı ve aynı tipi taşır; yine de uyumsuzdur, çünkü özel üyeler yalnızca aynı bildirimden geldiklerinde uyumlu sayılır. Bu, sınıflara adsal tiplemeye yakın bir davranış kazandırır.
Nesne tipleri için karşılık gelen teknik markalı tiptir (branded type): tipe, çalışma zamanında var olmayan bir alan eklenir.
declare const marka: unique symbol; type SensorKimligi = string & { readonly [marka]: "sensor" }; function sensorKimligi(ham: string): SensorKimligi { if (!/^s-\d{2}$/.test(ham)) { throw new Error(`gecersiz sensor kimligi: ${ham}`); } return ham as SensorKimligi; } function olcumOku(kimlik: SensorKimligi): string { return `${kimlik} okundu`; } console.log(olcumOku(sensorKimligi("s-01"))); const dizgi: string = sensorKimligi("s-02"); console.log(dizgi.toUpperCase());
Çıktı:
s-01 okundu S-02
Dosyanın sonuna, bir boş satırdan sonra olcumOku("s-03"); satırı eklenirse:
marka2.ts(21,10): error TS2345: Argument of type 'string' is not assignable to parameter of type 'SensorKimligi'.
Type 'string' is not assignable to type '{ readonly [marka]: "sensor"; }'.
Yapı üç parçadan oluşur. declare const marka: unique symbol bildirimi, benzersiz bir tip
düzeyi anahtar üretir — declare yazıldığı için çalışma zamanında karşılığı yoktur.
SensorKimligi tipi, dizgi ile o anahtarı taşıyan bir nesnenin kesişimidir; hiçbir
gerçek dizgi bu alanı taşımaz, dolayısıyla tipe yalnızca tip iddiasıyla girilebilir.
sensorKimligi fonksiyonu bu iddiayı tek bir yerde, doğrulamadan sonra yapar.
Sonuç, bir kanıtın tip sisteminde taşınmasıdır: SensorKimligi tipinde bir değer, biçim
denetiminden geçmiş demektir. dizgi bildiriminin geçerli olması da tasarımın parçasıdır
— markalı tip yine bir dizgidir ve dizgi işlemleri kullanılabilir; ters yön kapalıdır.
Bu kalıp, ilk derste konan kurala uyar: doğrulama çalışma zamanında yapılır, tip sistemi yalnızca sonucu kaydeder.
Özet
- Yapısal tiplemede uyumluluk, bildirilen ada değil taşınan üyelere bakılarak belirlenir; ilgisiz iki tip aynı biçimdeyse uyumludur.
- Fazlalık özellik denetimi yalnızca doğrudan yazılan nesne sabitlerinde uygulanır; ara değişkenden gelen nesnelerde uygulanmaz.
- Fonksiyon parametrelerinde uyum ters yönde çalışır: daha geniş parametre kabul eden fonksiyon, daha dar parametre bildiren tipe atanabilir.
- Yöntem sözdizimiyle bildirilen üyelerde bu kural gevşetilmiştir ve çalışma zamanı hatası üretebilen bir boşluk bırakır.
- Sınıflardaki özel üyeler uyumluluğu kırar; nesne tipleri için markalı tip kullanılır ve bu kalıp bir doğrulamanın yapıldığını tip düzeyinde kaydeder.
Sonraki Adım
Bu konudaki tiplerin tümü belirli ve sabitti: Olcum kayıtları tutan bir defter, Olcum
alan bir süzgeç. Oysa defter yapısının kayıt tipinden bağımsız olması gerekir — aynı kod
ölçümleri, uyarıları veya olayları toplamalıdır. Sonraki konu tip parametrelerini
tanıtacak ve bu bağımsızlığı, tip güvencesini kaybetmeden kuracak.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.