Ders 16 / 23
Şablon Dizgi Tipleri
Dizgi biçimlerinin tiple ifadesi, birleşimler üzerinde çapraz çarpım, yerleşik dizgi dönüşümleri, infer ile biçim ayrıştırma, açık uçlu kalıplar ve sabit tipin string içinde erimesi.
İçindekiler
Eşlenmiş tipler dersinde `oku${Capitalize<string & A>}` yazımı geçti ve bir alan adı
hesaplandı. Şablon dizgi tipi (template literal type), dizgi sabit tiplerini
birleştirerek yeni sabit tipler üretir.
Yazımı JavaScript’in şablon dizgilerinin aynısıdır, ancak değerler değil tipler birleştirilir. Bu ders, bu araçla dizgi biçimlerinin nasıl tiplendiğini, birleşimler üzerindeki davranışını ve sınırlarını ele alır.
Biçim Tanımlama
Ölçüm kayıtları çoğu zaman bir kanal adıyla anılır: hangi sensörden, hangi birimde. Bu ad bir biçim taşır ve biçim tiplenebilir:
type Sensor = "sicaklik" | "basinc"; type Birim = "C" | "Pa"; type Kanal = `${Sensor}.${Birim}`; const kanallar: Kanal[] = ["sicaklik.C", "sicaklik.Pa", "basinc.C", "basinc.Pa"]; console.log(kanallar.length, kanallar[0]);
Çıktı 4 sicaklik.Ctir. Dosyanın sonuna, bir boş satırdan sonra
const yanlis: Kanal = "sicaklik.%"; satırı eklenirse:
q1.ts(9,7): error TS2820: Type '"sicaklik.%"' is not assignable to type '"sicaklik.C" | "sicaklik.Pa" | "basinc.C" | "basinc.Pa"'. Did you mean '"sicaklik.C"'?
Tanı, hesabın sonucunu açıkça yazıyor: Kanal tipi dört sabit tipin birleşimidir. Yerine
koyulan her tip bir birleşimse, sonuç bu birleşimlerin çapraz çarpımıdır — iki sensör
ve iki birim dört kanal verir.
Tanının sonundaki öneri de derleyicinin sabit tip birleşimlerinde yaptığı bir kolaylıktır: yazım hatası olma olasılığı yüksek olan değerler için en yakın geçerli sabiti gösterir.
Bu tip, dizginin yalnızca bir dizgi olmadığını, belirli bir biçim taşıdığını kaydeder. Yapısal Tip Uyumluluğu dersindeki markalı tiple aynı amaca hizmet eder, ama biçim gerçekten tanımlanabildiği için tip iddiasına gerek kalmaz.
Yerleşik Dizgi Dönüşümleri
Derleyici dört dizgi dönüşümünü yerleşik olarak sağlar: Uppercase, Lowercase,
Capitalize ve Uncapitalize.
type Sensor = "sicaklik" | "basinc"; type BuyukSensor = Uppercase<Sensor>; type OlaySensorAdi = `olcum${Capitalize<Sensor>}Alindi`; const buyuk: BuyukSensor = "SICAKLIK"; const olay: OlaySensorAdi = "olcumBasincAlindi"; console.log(buyuk, olay);
Çıktı SICAKLIK olcumBasincAlindidir. Dosyanın sonuna, bir boş satırdan sonra
const yanlisOlay: OlaySensorAdi = "olcumbasincAlindi"; satırı eklenirse:
q3.ts(10,7): error TS2820: Type '"olcumbasincAlindi"' is not assignable to type '"olcumSicaklikAlindi" | "olcumBasincAlindi"'. Did you mean '"olcumBasincAlindi"'?
Bu dönüşümler derleyicinin içine gömülüdür; tip düzeyinde yazılamazlar. Harf dönüşümleri
yerel ayardan bağımsız, sabit bir kurala göre yapılır — dolayısıyla Türkçedeki noktasız
ı ve noktalı i ayrımı gibi yerel kurallar burada geçerli değildir. Kanal ve olay
adlarında ASCII kalmanın bir nedeni de budur.
Anahtar Tablosu Kurmak
Şablon dizgi tipi bir eşlenmiş tipin anahtar kaynağı olabilir. Sonuç, biçimi tanımlı ve tam bir tablodur:
type Sensor = "sicaklik" | "basinc"; type Birim = "C" | "Pa"; type Kanal = `${Sensor}.${Birim}`; type KanalTablosu = { [K in Kanal]: number; }; const sonDegerler: KanalTablosu = { "sicaklik.C": 21.4, "sicaklik.Pa": 0, "basinc.C": 0, "basinc.Pa": 101325, }; console.log(sonDegerler["basinc.Pa"], Object.keys(sonDegerler).length);
Çıktı 101325 4tür.
Tablonun anahtarları elle sayılmamıştır; Sensor birleşimine üçüncü bir sensör
eklendiğinde tablo eksik kalır ve derleyici tanı verir. Ölçüm modelinin genişlemesi,
modeli kullanan veri yapılarının güncellenmesini zorunlu kılar.
infer ile Biçim Ayrıştırma
Şablon dizgi tipleri koşullu tiplerin sol tarafında da kullanılabilir. infer ile biçimin
parçaları çıkarılır:
type SensorAdi<K> = K extends `${infer S}.${string}` ? S : never; type BirimAdi<K> = K extends `${string}.${infer B}` ? B : never; type Kanal = "sicaklik.C" | "basinc.Pa"; declare const s: SensorAdi<Kanal>; declare const b: BirimAdi<Kanal>; const x: null = s; const y: null = b;
q5.ts(9,7): error TS2322: Type '"sicaklik" | "basinc"' is not assignable to type 'null'. Type '"sicaklik"' is not assignable to type 'null'. q5.ts(10,7): error TS2322: Type '"C" | "Pa"' is not assignable to type 'null'. Type '"C"' is not assignable to type 'null'.
Ayrıştırma dağılımla birlikte çalışmıştır: Kanal birleşiminin her üyesi ayrı
eşleştirilmiş, sonuçların birleşimi alınmıştır.
Bu tip, çalışma zamanındaki bir ayrıştırmanın imzasında kullanılabilir:
type Sensor = "sicaklik" | "basinc"; type Birim = "C" | "Pa"; type Kanal = `${Sensor}.${Birim}`; type SensorAdi<K extends Kanal> = K extends `${infer S}.${string}` ? S : never; function sensorAdi<K extends Kanal>(kanal: K): SensorAdi<K> { return kanal.split(".")[0] as SensorAdi<K>; } const ad = sensorAdi("basinc.Pa"); console.log(ad);
Çıktı basinctır. Dosyanın sonuna, bir boş satırdan sonra
const yanlisAtama: "sicaklik" = sensorAdi("basinc.Pa"); satırı eklenirse:
q6.ts(14,7): error TS2322: Type '"basinc"' is not assignable to type '"sicaklik"'.
sensorAdi("basinc.Pa") çağrısının tipi string değil "basinc"tır. Çağrı yerinde
verilen sabit tipten, dönüş tipi hesaplanmıştır.
Gövdedeki as SensorAdi<K> yazımına dikkat: önceki derste kurulan sınır burada da
geçerlidir, koşullu dönüş tipi gövde içinde çözülmez. Bu, imzada verilen sözü gövdenin
taşıdığını kabul eden bir borçtur ve tek satırda toplandığı için sınanabilir durumdadır.
Açık Uçlu Kalıplar
Yerine koyulan tipin bir birleşim olması zorunlu değildir; string de yazılabilir. Sonuç,
sabit bir küme değil bir biçim kalıbıdır:
type OlcumOlayi = `olcum:${string}`; type UyariOlayi = `uyari:${string}`; type Olay = OlcumOlayi | UyariOlayi; function yonlendir(olay: Olay): string { if (olay.startsWith("olcum:")) { return `kayda alindi: ${olay}`; } return `bildirildi: ${olay}`; } console.log(yonlendir("olcum:alindi")); console.log(yonlendir("uyari:esik-asildi"));
Çıktı:
kayda alindi: olcum:alindi bildirildi: uyari:esik-asildi
`olcum:${string}` tipi, "olcum:" ile başlayan her dizgiyi kapsar. Kapalı bir
küme olmadığı için önceki bölümdeki gibi tek tek sayılamaz; buna karşılık ön ek zorunluluğu
korunur. Dosyanın sonuna, bir boş satırdan sonra const yanlis: OlcumOlayi = "uyari:alindi";
satırı eklenirse:
u12.ts(15,7): error TS2322: Type '"uyari:alindi"' is not assignable to type '`olcum:${string}`'.
Bu kalıp, olay adları veya anahtar önekleri gibi gövdesi serbest ama biçimi bağlayıcı dizgilerde kullanılır. Sabit bir liste tutmak gerekmez; ön ek yanlış yazıldığında derleyici bildirir.
Sabit Tipin string İçinde Erimesi
Açık uçlu kalıplarla birlikte gelen bir tuzak vardır. Bir sabit tip, string ile aynı
birleşime konduğunda kaybolur:
type Kanal = "sicaklik.C" | string; declare const k: Kanal; const x: null = k;
u8.ts(4,7): error TS2322: Type 'string' is not assignable to type 'null'.
Tanı Kanal yerine string yazıyor: "sicaklik.C" üyesi zaten string kümesinin içinde
olduğu için birleşim stringe indirgenmiştir. Sonuç, hiçbir kısıt taşımayan bir tiptir —
oysa yazılış, sanki bir öneri listesi sunuyormuş izlenimi verir.
Bu, “bilinen değerler ve serbest metin” modellemek istendiğinde sık düşülen bir hatadır. Doğru araç, kısıtın gerçekten istendiği yerde kapalı bir birleşim ya da açık uçlu bir kalıp kullanmaktır:
type Kanal = `${"sicaklik" | "basinc"}.${"C" | "Pa"}`; function birimAyristir(kanal: Kanal): string { return kanal.split(".")[1]; } const kanallar: Kanal[] = ["sicaklik.C", "basinc.Pa"]; console.log(kanallar.map(birimAyristir).join(","));
Çıktı C,Padır. Dosyanın sonuna, bir boş satırdan sonra şu iki satır eklenirse:
const serbest: string = "sicaklik.C"; birimAyristir(serbest);
u13.ts(11,15): error TS2345: Argument of type 'string' is not assignable to parameter of type '"sicaklik.C" | "sicaklik.Pa" | "basinc.C" | "basinc.Pa"'.
Sabit tiplerin kaybolmadığı görülüyor: tip hâlâ dört değerli kapalı bir kümedir ve serbest
bir dizgi kabul edilmez. Sabit tip birleşimine string eklemek, tam da bu güvenceyi
ortadan kaldıran işlemdir.
Birleşim Büyüklüğü Sınırı
Çapraz çarpım hızlı büyür. İki üç üyeli birleşim dokuz sonuç verir; her ek bileşen çarpanı artırır. Derleyicinin bir üst sınırı vardır:
type Harf = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j"; type Uclu = `${Harf}${Harf}${Harf}${Harf}${Harf}`; declare const x: Uclu; const y: null = x;
q9.ts(2,13): error TS2590: Expression produces a union type that is too complex to represent.
On harfin beşli bileşimi yüz bin sabit tip demektir ve derleyici bunu reddeder. Sınır sürüme göre değişebilir; asıl ders, biçimin kapalı olduğu durumlarda şablon dizgi tipinin uygun, açık olduğu durumlarda uygunsuz olmasıdır.
Algoritmalar kursundaki kombinatoryel patlama kavramı burada tip denetleyicisinin kendisinde ortaya çıkar. Serbest biçimli dizgiler için doğru araç, sabit tip birleşimi değil çalışma zamanı doğrulaması ve markalı tiptir.
Özet
- Şablon dizgi tipi, sabit tipleri birleştirerek dizgi biçimlerini tip düzeyinde tanımlar; yerine koyulan tipler birleşimse sonuç, birleşimlerin çapraz çarpımıdır.
Uppercase,Lowercase,CapitalizeveUncapitalizederleyiciye gömülüdür ve yerel ayardan bağımsız çalışır.- Şablon dizgi tipi bir eşlenmiş tipin anahtar kaynağı olarak kullanıldığında, biçimi
tanımlı ve tam bir tablo elde edilir; koşullu tipin sol tarafında
inferile biçim parçaları çıkarılır ve sonuç birleşimler üzerinde dağılır. - Yerine
stringkonduğunda tip kapalı bir küme değil açık uçlu bir biçim kalıbı olur; ön ek zorunluluğu korunur, değerler sayılamaz. - Bir sabit tip
stringile aynı birleşime konduğunda erir ve birleşimstringe indirgenir; “bilinen değerler ve serbest metin” bu yolla modellenemez. - Çapraz çarpım hızla büyür ve derleyici belirli bir büyüklükten sonra
TS2590tanısı verir; kapalı sabit tip kümeleri yalnızca sınırlı büyüklükte kurulabilir.
Sonraki Adım
Bu konuda elle yazılan dönüşümlerin çoğu — salt okunur sürüm, seçmeli sürüm, birleşimden üye eleme — standart kitaplıkta hazır tiplerle bulunur. Sonraki ders bu yardımcı tipleri tanıtacak, hangilerinin hangi mekanizmayla kurulduğunu gösterecek ve ölçüm modelinin türevlerini bunlarla yeniden yazacak.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.