İçeriğe geç
academia.sh

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, Capitalize ve Uncapitalize derleyiciye 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 infer ile biçim parçaları çıkarılır ve sonuç birleşimler üzerinde dağılır.
  • Yerine string konduğ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 string ile aynı birleşime konduğunda erir ve birleşim stringe 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 TS2590 tanı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.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat