İçeriğe geç
TypeScript

TypeScript Template Literal Types

Tarık Tunç
TypeScript Template Literal Types

TypeScript Template Literal Types

Template literal type'lar, TypeScript 4.1 ile tanıtılan ve string literal type'ları programatik olarak oluşturmanızı sağlayan bir özelliktir. JavaScript template literal'larının tip seviyesindeki karşılığıdır. Bu yazıda template literal type'ların kullanımını ve pratik uygulamalarını detaylı örneklerle ele alacağız.

Temel Kullanım

// Basit template literal type
type Selamlama = `Merhaba, ${string}`;

let mesaj1: Selamlama = "Merhaba, Tarik";   // Geçerli
let mesaj2: Selamlama = "Merhaba, Dünya";   // Geçerli
// let mesaj3: Selamlama = "Günaydın, Tarik"; // HATA!

// Union ile template literal
type Renk = "kirmizi" | "mavi" | "yesil";
type Boyut = "kucuk" | "orta" | "buyuk";

type RenkBoyut = `${Renk}-${Boyut}`;
// "kirmizi-kucuk" | "kirmizi-orta" | "kirmizi-buyuk" |
// "mavi-kucuk" | "mavi-orta" | "mavi-buyuk" |
// "yesil-kucuk" | "yesil-orta" | "yesil-buyuk"

// Event adları
type Olay = "click" | "scroll" | "mousemove";
type OlayHandler = `on${Capitalize<Olay>}`;
// "onClick" | "onScroll" | "onMousemove"

Yerleşik String Manipulation Types

// Uppercase - tüm harfleri büyük yapar
type Buyuk = Uppercase<"merhaba">; // "MERHABA"

// Lowercase - tüm harfleri küçük yapar
type Kucuk = Lowercase<"MERHABA">; // "merhaba"

// Capitalize - ilk harfi büyük yapar
type BasHarfBuyuk = Capitalize<"merhaba">; // "Merhaba"

// Uncapitalize - ilk harfi küçük yapar
type BasHarfKucuk = Uncapitalize<"Merhaba">; // "merhaba"

// Kombinasyon
type CSSProperty = "backgroundColor" | "fontSize" | "borderRadius";
type CSSKebab = {
  [K in CSSProperty as Uncapitalize<K>]: string;
};

// Getter/Setter oluşturma
type PropMethods<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
} & {
  [K in keyof T as `set${Capitalize<string & K>}`]: (value: T[K]) => void;
};

interface Kullanici {
  isim: string;
  yas: number;
}

type KullaniciMethods = PropMethods<Kullanici>;
// {
//   getIsim: () => string;
//   getYas: () => number;
//   setIsim: (value: string) => void;
//   setYas: (value: number) => void;
// }
İpucu: String manipulation type'lar (Uppercase, Lowercase, Capitalize, Uncapitalize) yalnızca tip seviyesinde çalışır; çalışma zamanında string dönüşümü yapmazlar. Runtime dönüşüm için JavaScript string metotlarını kullanın.

Pattern Matching ile Template Literal

// String pattern matching
type ExtractRouteParams<T extends string> =
  T extends `${string}:${infer Param}/${infer Rest}`
    ? Param | ExtractRouteParams<`/${Rest}`>
    : T extends `${string}:${infer Param}`
    ? Param
    : never;

type Params1 = ExtractRouteParams<"/kullanicilar/:id">;
// "id"

type Params2 = ExtractRouteParams<"/kullanicilar/:userId/yazilar/:postId">;
// "userId" | "postId"

// CSS değer parser
type ParsePixel<T extends string> =
  T extends `${infer N}px` ? N : never;

type A = ParsePixel<"16px">; // "16"
type B = ParsePixel<"auto">; // never

// Dot notation path
type PathOf<T, Prefix extends string = ""> = {
  [K in keyof T & string]: T[K] extends object
    ? PathOf<T[K], `${Prefix}${K}.`>
    : `${Prefix}${K}`;
}[keyof T & string];

interface Ayarlar {
  tema: {
    renk: string;
    font: string;
  };
  dil: string;
}

type AyarPathi = PathOf<Ayarlar>;
// "tema.renk" | "tema.font" | "dil"

Pratik Uygulamalar

// Tip güvenli event emitter
type EventMap = {
  kullaniciGiris: { userId: number; zaman: Date };
  kullaniciCikis: { userId: number };
  siparisOlusturuldu: { siparisId: string; tutar: number };
};

type EventAdi = keyof EventMap;
type EventCallback<K extends EventAdi> = (data: EventMap[K]) => void;

class TipGuvenliEmitter {
  private listeners = new Map<string, Function[]>();

  on<K extends EventAdi>(event: K, callback: EventCallback<K>): void {
    const existing = this.listeners.get(event) || [];
    this.listeners.set(event, [...existing, callback]);
  }

  emit<K extends EventAdi>(event: K, data: EventMap[K]): void {
    const callbacks = this.listeners.get(event) || [];
    callbacks.forEach(cb => cb(data));
  }
}

const emitter = new TipGuvenliEmitter();
emitter.on("kullaniciGiris", (data) => {
  console.log(data.userId); // Tip güvenli!
});

// SQL sorgu builder (basitleştirilmiş)
type TableName = "kullanicilar" | "urunler" | "siparisler";
type SelectQuery = `SELECT * FROM ${TableName}`;
type WhereQuery = `${SelectQuery} WHERE ${string}`;
type OrderQuery = `${SelectQuery | WhereQuery} ORDER BY ${string}`;
Uyarı: Template literal type'lar ile çok fazla union kombinasyonu oluşturmak, TypeScript derleyicisinin performansını ciddi şekilde etkileyebilir. Örneğin, 100 elemanlı iki union'ı birleştirmek 10.000 olası kombinasyon oluşturur. Union boyutlarını makul seviyede tutun.
Bilgi: Template literal type'lar, özellikle kütüphane geliştiricileri için çok değerlidir. Express route'ları, GraphQL sorguları, CSS-in-JS ve veritabanı sorguları gibi string tabanlı API'lere tip güvenliği eklemenizi sağlar.

Sonuç

Template literal type'lar, TypeScript tip sistemine string manipülasyonu yeteneği kazandırır. Basit string birleştirmelerden karmaşık pattern matching'e kadar geniş bir kullanım alanına sahiptir. String manipulation type'lar, mapped type'lar ve conditional type'lar ile birlikte kullanıldığında, inanılmaz derecede ifade gücü yüksek tip tanımlamaları yapmanızı sağlar.

Kaynaklar

İlgili Yazılar