Ders 14 / 23
Eşlenmiş Tipler
Var olan bir tipten yeni tip üretme; alan değiştiricilerinin eklenmesi ve kaldırılması, anahtar yeniden eşlemesi, alan süzme ve eşlemenin dizi ile demetler üzerindeki davranışı.
İçindekiler
Ölçüm kaydının değişik biçimleri şimdiye kadar elle yazıldı: arşivlenmiş kayıtlar için salt okunur bir sürüm, güncelleme isteği için alanları seçmeli bir sürüm, listeleme için yalnızca birkaç alanı taşıyan bir özet.
Her biri asıl tipin bir dönüşümüdür. Elle yazıldıklarında asıl tip değiştiğinde birlikte güncellenmeleri gerekir ve bu, unutulmaya elverişli bir bakım borcudur. Eşlenmiş tip (mapped type), dönüşümü tip düzeyinde hesaplar: alan listesi tek yerde durur, türevler kendiliğinden güncellenir.
Temel Yazım
Eşlenmiş tip, bir anahtar kümesi üzerinde dolaşan bir döngüye benzer:
interface Olcum { kimlik: string; deger: number; birim: "C" | "Pa" | "%"; } type SaltOkunur<T> = { readonly [A in keyof T]: T[A] }; type Yamalanabilir<T> = { [A in keyof T]?: T[A] }; const arsivKayit: SaltOkunur<Olcum> = { kimlik: "s-01", deger: 21.4, birim: "C", }; console.log(arsivKayit.deger); const yama: Yamalanabilir<Olcum> = { deger: 22.1 }; console.log(yama.deger, yama.kimlik);
Çıktı:
21.4 22.1 undefined
Dosyanın sonuna, bir boş satırdan sonra arsivKayit.deger = 22.1; satırı eklenirse:
o2.ts(20,12): error TS2540: Cannot assign to 'deger' because it is a read-only property.
Yazımın parçaları şunlardır. [A in keyof T] bölümü, keyof T birleşiminin her üyesini
sırayla A adına bağlar — Programlama Temelleri kursundaki for döngüsünün tip
düzeyindeki karşılığı. İki nokta üstündeki T[A], o alanın asıl tipidir. Başa yazılan
readonly ve ada eklenen ?, her alana uygulanan değiştiricilerdir.
Yamalanabilir<Olcum> tipinin bütün alanları seçmeli olduğu için { deger: 22.1 } sabiti
kabul edilir; yama.kimlik erişiminin tipi string | undefineddır ve çıktıda undefined
görünür.
Bu iki dönüşümün karşılığı standart kitaplıkta zaten bulunur (Readonly ve Partial);
Yardımcı Tipler dersinde ele alınacaklardır. Burada elle yazılmalarının nedeni,
mekanizmanın görünür olmasıdır.
Değiştirici Kaldırma
Değiştiriciler eklenebildiği gibi kaldırılabilir de. Kaldırma, değiştiricinin önüne eksi işareti konarak yazılır:
interface Olcum { readonly kimlik: string; readonly deger?: number; } type Cozulmus<T> = { -readonly [A in keyof T]-?: T[A] }; const tam: Cozulmus<Olcum> = { kimlik: "s-01", deger: 21.4 }; tam.deger = 22.1; console.log(tam.deger.toFixed(1));
Çıktı 22.1dir. Dosyanın sonuna, bir boş satırdan sonra
const eksik: Cozulmus<Olcum> = { kimlik: "s-02" }; satırı eklenirse:
o4.ts(12,7): error TS2741: Property 'deger' is missing in type '{ kimlik: string; }' but required in type 'Cozulmus<Olcum>'.
Olcum tipinde kimlik salt okunur, deger hem salt okunur hem seçmeliydi.
Cozulmus<Olcum> tipinde ikisi de yazılabilir ve zorunludur. tam.deger.toFixed(1)
çağrısının daraltma gerektirmemesi bunun sonucudur: alan artık numberdır,
number | undefined değil.
Bu dönüşümün kullanım yeri, kısmi bir kaydın tamamlanmış hâlini temsil etmektir. Varsayılan değerler uygulandıktan sonra elde edilen kayıt için ayrı bir tip yazmak yerine, asıl tipten hesaplanır.
Anahtar Yeniden Eşlemesi
as yazımı, üretilen alanın adını değiştirmeyi sağlar:
interface Olcum { kimlik: string; deger: number; birim: "C" | "Pa" | "%"; } type Okuyucular<T> = { [A in keyof T as `oku${Capitalize<string & A>}`]: () => T[A]; }; const okuyucu: Okuyucular<Olcum> = { okuKimlik: () => "s-01", okuDeger: () => 21.4, okuBirim: () => "C", }; console.log(okuyucu.okuDeger().toFixed(1), okuyucu.okuKimlik());
Çıktı 21.4 s-01dir.
Üretilen tip, her alan için bir okuyucu fonksiyon bildirir ve dönüş tipini asıl alandan
alır: okuDeger çağrısı number, okuBirim çağrısı "C" | "Pa" | "%" verir. Ad
üretiminde kullanılan şablon dizgi yazımı ile Capitalize dönüşümü sonraki dersin
konusudur; burada yalnızca adın hesaplanabildiği görülmelidir.
Yeniden eşlemenin ikinci ve daha güçlü kullanımı, bir alanı elemektir. Üretilen ad
never olduğunda o alan çıktıya girmez:
interface Olcum { kimlik: string; deger: number; birim: "C" | "Pa" | "%"; zaman: number; } type SayisalAlanlar<T> = { [A in keyof T as T[A] extends number ? A : never]: T[A]; }; const sayisal: SayisalAlanlar<Olcum> = { deger: 21.4, zaman: 1706000000000 }; console.log(sayisal.deger + sayisal.zaman);
Çıktı 1706000000021.4tür. Dosyanın sonuna, bir boş satırdan sonra
const yanlis: SayisalAlanlar<Olcum> = { deger: 21.4, zaman: 1, kimlik: "s-01" }; satırı
eklenirse:
o7.ts(15,64): error TS2353: Object literal may only specify known properties, and 'kimlik' does not exist in type 'SayisalAlanlar<Olcum>'.
Sonuç tipi yalnızca deger ve zaman alanlarını taşır; kimlik ve birim elenmiştir.
T[A] extends number ? A : never yazımı bir koşullu tiptir ve sonraki derslerden
birinin konusudur; burada işlevi, alan tipine bakarak adı tutmak veya never üretip
elemektir.
Bu tip, ölçüm kaydının sayısal özetini çıkaran kodun imzasında kullanılabilir. Kayda yeni bir sayısal alan eklendiğinde özet tipi kendiliğinden büyür; dizgi bir alan eklendiğinde etkilenmez.
Diziler ve Demetler Üzerinde Eşleme
keyof T üzerinde dolaşan bir eşleme diziye uygulandığında beklenmedik bir şey olmaz:
sonuç yine bir dizidir.
type SaltOkunur<T> = { readonly [A in keyof T]: T[A] }; const degerler: SaltOkunur<number[]> = [21.4, 22.1]; const aralik: SaltOkunur<[alt: number, ust: number]> = [-40, 85]; console.log(degerler.length, degerler[0], aralik[1]); console.log(degerler.map((d) => d * 2));
Çıktı:
2 21.4 85 [ 42.8, 44.2 ]
SaltOkunur<number[]> tipi readonly number[], SaltOkunur<[number, number]> tipi salt
okunur bir demettir. Dizi yöntemleri (map, length, indeksli erişim) kullanılabilir
durumda kalmış, değiştirme yöntemleri kalkmıştır:
type SaltOkunur<T> = { readonly [A in keyof T]: T[A] }; const degerler: SaltOkunur<number[]> = [21.4, 22.1]; degerler.push(23.0);
u4.ts(4,10): error TS2339: Property 'push' does not exist on type 'readonly number[]'.
Bu davranış, eşlemenin kaynağı doğrudan keyof T olduğunda geçerlidir. Derleyici bu
biçimi tanır ve dizi ile demetleri kendi türlerinde tutar. Kaynak başka bir ifadeyle
yazıldığında tanıma bozulur:
type EslemA<T> = { readonly [A in keyof T]: T[A] }; type EslemB<T> = { readonly [A in keyof T & string]: T[A] }; declare const a: EslemA<number[]>; declare const b: EslemB<number[]>; const x: null = a; const y: null = b;
u11.ts(7,7): error TS2322: Type 'readonly number[]' is not assignable to type 'null'. u11.ts(8,7): error TS2322: Type 'EslemB<number[]>' is not assignable to type 'null'.
Tanıların farkı belirleyicidir. EslemA<number[]> tipi readonly number[] olarak
çözülmüştür — derleyici sonucu bir dizi olarak yazıyor. EslemB<number[]> ise
çözülmemiş bir eşleme olarak kalmıştır; dizi yapısı kaybolmuş, yerine dizinin özellik
adlarını taşıyan sıradan bir nesne tipi geçmiştir.
Pratik kural: bir tipin yapısını korumak isteniyorsa eşleme kaynağı keyof T olarak
yazılır. Anahtar süzmek gerekiyorsa süzme, as yeniden eşlemesiyle yapılır — kaynak
keyof T kalır, ad hesabı sağ tarafta olur.
Herhangi Bir Birleşim Üzerinde Eşleme
Eşleme kaynağının keyof T olması zorunlu değildir; herhangi bir dizgi birleşimi
kullanılabilir:
type Birim = "C" | "Pa" | "%"; type Araliklar = { [B in Birim]: [alt: number, ust: number]; }; const araliklar: Araliklar = { C: [-40, 85], Pa: [0, 200000], "%": [0, 100], }; console.log(araliklar.C[1], araliklar["%"][1]);
Çıktı 85 100dür. Dosyanın sonuna, bir boş satırdan sonra
const eksik: Araliklar = { C: [-40, 85], Pa: [0, 200000] }; satırı eklenirse:
o9.ts(15,7): error TS2741: Property '"%"' is missing in type '{ C: [number, number]; Pa: [number, number]; }' but required in type 'Araliklar'.
Bu, Arayüzler dersinde eleştirilen indeks imzasının doğru alternatifidir. İndeks imzası “her dizgi anahtar geçerlidir” der ve olmayan anahtara erişimi hata saymazdı. Eşlenmiş tip ise anahtar kümesini tam olarak belirler: eksik bir anahtar tanı verir, fazladan bir anahtar da öyle.
Değer kümesi Birim birleşimine yeni bir birim eklendiğinde, aralık tablosunu güncellemek
zorunlu hâle gelir. Ayırt edici birleşimde never ile kurulan tükenmişlik denetiminin,
veri tabloları için karşılığıdır.
Özet
- Eşlenmiş tip, bir anahtar kümesi üzerinde dolaşarak yeni bir nesne tipi üretir; türev tipler asıl tiple birlikte güncellenir.
readonlyve?değiştiricileri eşleme sırasında eklenebilir;-readonlyve-?yazımlarıyla kaldırılabilir.asyazımı üretilen alanın adını hesaplar; adneverolduğunda alan çıktıdan elenir.- Eşleme kaynağı
keyof Tolmak zorunda değildir; herhangi bir dizgi birleşimi üzerinde eşleme yapılabilir. - Eşleme kaynağı doğrudan
keyof Tolduğunda dizi ve demetler kendi türlerinde kalır; kaynak başka bir ifadeyle yazıldığında bu yapı kaybolur. - Sabit anahtar kümesi için eşlenmiş tip, indeks imzasından güvenlidir: eksik ve fazla anahtarların ikisi de bildirilir.
Sonraki Adım
Önceki iki örnekte T[A] extends number ? A : never biçiminde bir yazım geçti ve tip
düzeyinde bir dallanma yaptı. Bu, tip sisteminin hesaplama yeteneğinin merkezindeki
yapıdır. Sonraki ders koşullu tipleri, dağılım kurallarını ve bir tipin içinden parça
çıkaran infer yazımını ele alacak.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.