iToverDose/Yazılım· 1 TEMMUZ 2026 · 20:04

TypeScript'te Aşırı Yüklemelerin Tamamını Türetmek: Sıralamanın Önemi

TypeScript’in aşırı yüklemeli fonksiyonlardan yalnızca sonuncusunu türettiğini biliyor muydunuz? Sıralamayı değiştirmek, tüm çağrı imzalarını elde etmenin kapısını açıyor. Nasıl çalıştığını keşfedin.

DEV Community3 dk okuma0 Yorumlar

TypeScript’te fonksiyonları dinamik olarak geliştirirken, bazen tüm aşırı yüklemeleri (overloads) yakalamak ve bunları yeniden yapılandırmak gerekiyor. Bu süreç, özellikle i18next gibi yerelleştirme kütüphanelerinde çeviri anahtarlarını daha akıllı bir şekilde yönetmek istediğinizde kritik hale geliyor. Basit bir string yerine fonksiyonel bir selector kullanarak, geliştirici deneyimini (DX) nasıl artırabileceğinizi merak ettiniz mi? İşte TypeScript’in bu konudaki sınırlarını zorlayan bir hikaye.

Fonksiyonel Selector’larla Dil Dosyalarına Yeni Bir Bakış

Çeviri anahtarlarını string olarak geçmek yerine, fonksiyonel bir selector kullanmak mümkün:

translate($ => $.user.profile.title);

Bu yaklaşım, arka planda bir Proxy aracılığıyla çalışıyor ve tüm üye erişim yolunu çalışma zamanında okuyor. Peki, neden bu kadar önemli?

  • Kaynağa doğrudan erişim: JSON ya da şema alanına gidebilirsiniz.
  • Çeviri şemasını kolayca bulabilirsiniz.
  • Editörünüzdeki yeniden adlandırma işlemleriyle tüm alanları güncelleyebilirsiniz.
  • Tüm çeviri ağacında otomatik tamamlama desteği alırsınız.

Bu fikir, geliştirici deneyimini (DX) önemli ölçüde iyileştirirken, teknik olarak bazı zorlukları da beraberinde getiriyor.

Aşırı Yüklemeli Fonksiyonlardan Tüm İmzaları Çıkarmak

Sıklıkla, mevcut bir fonksiyon tipinin argümanlarından birini değiştirmek ve geri kalan API’sını korumak gerekiyor. Örneğin, i18next kütüphanesindeki translate fonksiyonunun tipini genişletmek istiyorsunuz. Ancak bu fonksiyonun birçok aşırı yükleme imzası olduğunu fark ediyorsunuz.

Burada karşılaşılan temel soru şu:

Bir aşırı yüklemeli fonksiyon tipinden tüm çağrı imzalarını çıkarabilir, bunları dönüştürebilir ve ardından orijinal API’yi koruyacak şekilde yeniden oluşturabilir misiniz?

Bu sorunun cevabını ararken, çoğu kaynaktaki çözümlerin genellikle "en fazla 5 ya da 10 aşırı yüklemeyi destekle" gibi sınırlamalarla karşılaşıyorsunuz. Peki, bu sınırlamayı aşmanın bir yolu var mı?

TypeScript’in Sadece Son İmzayı Türetmesi

Basit bir yardımcı tip tanımlayarak başlayalım:

type InferCallSignature<T_Callable> =
  T_Callable extends (...args: infer T_Parameters) => infer T_Result
    ? ((...parameters: T_Parameters) => T_Result)
    : never;

Bu tip, verilen T_Callable tipinin parametrelerini ve dönüş değerini çıkarır. Ancak bu tip, aşırı yüklemeli fonksiyonlarla kullanıldığında ilginç bir davranış sergiliyor.

Bir aşırı yüklemeli fonksiyon tipi düşünün:

type A = {
  (): void;
  (a: number): string;
  (a: string, b: number): boolean;
};

Bu tip üzerinde InferCallSignature uyguladığınızda, karşınıza yalnızca son imza çıkıyor:

type B = InferCallSignature<A>; // (a: string, b: number) => boolean

TypeScript, aşırı yüklemeli fonksiyonlardan sadece sonuncu imzayı türetir. Bu, TypeScript’in dokümantasyonunda da belirtilen bir davranıştır. Peki, tüm imzaları nasıl elde edebiliriz?

Kesişim Sırasının Gücü

Bu noktada, kesişim (intersection) sırasının önemini fark ettik. TypeScript’in son imzayı türetmesi, kesişim sırasını değiştirerek manipüle edilebilir. Örneğin:

declare const aLeft: A & ((a: string, b: number) => boolean);
declare const aRight: ((a: string, b: number) => boolean) & A;

Bu iki ifade mantıksal olarak aynı görünse de, aRight tipinde ilk imza olarak (a: string, b: number) => boolean yer alır. Bu, kesişim sırasının TypeScript’in imza sıralamasını etkilediğini gösteriyor.

Dolayısıyla, InferCallSignature tipini kesişim içine yerleştirerek, tüm imzaları sırayla elde etmek mümkün hale geliyor.

Tüm İmzaları Türetmek İçin Bir Yaklaşım

Bu gözlemden yola çıkarak, tüm aşırı yüklemeli imzaları elde etmek için özyinelemeli (recursive) bir yaklaşım geliştirilebilir:

  1. Geçerli son imzayı çıkar.
  2. Bu imzayı bir kesişime ekle.
  3. TypeScript’in bir sonraki imzayı ortaya çıkarmasını sağla.
  4. Tüm imzalar elde edilene kadar bu işlemi tekrarla.

Sonuçta ortaya çıkan yardımcı tip şu şekilde olabilir:

type InternalCallSignatures<T_Callable, T_Alignment> =
  (
    { [key in keyof T_Callable]: T_Callable[key] } & T_Alignment
  ) extends T_Callable
    ? never
    : T_Callable extends (...args: infer T_Parameters) => infer T_Result
      ? (
          ((...parameters: T_Parameters) => T_Result) |
          InternalCallSignatures<
            T_Alignment & T_Callable,
            ((...parameters: T_Parameters) => T_Result) & T_Alignment
          >
        )
      : never;

export type CallSignatures<T_Callable> =
  InternalCallSignatures<T_Callable, {}>;

Bu tip, her aşamada bir imzayı çıkarır ve onu kesişime ekler. Bu süreç, tüm imzalar elde edilene kadar devam eder.

Sonuç: Geliştirici Deneyimini Yeniden Tanımlamak

TypeScript’in aşırı yüklemelerle ilgili bu davranışı, fonksiyonel selector’larla çalışırken oldukça kullanışlı hale geliyor. Artık, çeviri anahtarlarını string olarak geçmek yerine, fonksiyonel bir yaklaşım benimseyebilir ve tüm geliştirici araçlarından en üst düzeyde faydalanabilirsiniz.

Bu teknik, sadece i18next için değil, aşırı yüklemeleri optimize etmek isteyen herhangi bir TypeScript projesi için de değerli bir araç sunuyor. Gelecekte, bu yaklaşımın TypeScript ekosisteminde daha da yaygınlaşmasını ve geliştirici deneyimini daha da iyileştirmesini bekleyebiliriz.

Yapay zeka özeti

TypeScript’in aşırı yüklemeli fonksiyonlardan sadece sonuncusunu türettiğini biliyor muydunuz? Sıralamayı değiştirerek tüm imzaları nasıl yakalayabileceğinizi ve geliştirici deneyimini nasıl iyileştirebileceğinizi öğrenin.

Yorumlar

00
YORUM BIRAK
ID #A4C4PW

0 / 1200 KARAKTER

İnsan doğrulaması

5 + 6 = ?

Editör onayı sonrası yayına girer

Moderasyon · Spam koruması aktif

Henüz onaylı yorum yok. İlk yorumu sen bırak.