TypeScript'te generics, bir fonksiyonu, interface'i ya da sınıfı tek bir tipe bağlamadan yazmanızı sağlayan tip parametreleridir. function ilk<T>(dizi: T[]): T gibi bir tanımda T bir tip değişkenidir: fonksiyon çağrıldığında gerçek tiple (örneğin number) yer değiştirir ve dönüş tipi de buna göre belirlenir. Böylece any kullanmadan, tip bilgisini kaybetmeden yeniden kullanılabilir kod yazarsınız.
Bu yazıda generic fonksiyonları, extends ile kısıt (constraint) koymayı, varsayılan tip parametrelerini, generic interface ve sınıfları ve sık yapılan hataları TypeScript 6.0 derleyicisiyle denenmiş örneklerle anlatıyoruz. Kısa bir genel bakış isterseniz TypeScript ileri seviye özeti yazımız tüm tip manipülasyonu araçlarını tek sayfada özetliyor; burada generics konusunu ayrıntılı ele alıyoruz.
any yerine neden generic?

Bir dizinin ilk elemanını döndüren fonksiyonu düşünün. Parametreyi any[] yaparsanız her diziyi kabul eder ama dönüş değeri de any olur; sayı dizisi verdiğinizi derleyici artık bilmez. Generic sürümde ise giren tip, çıkan tipe taşınır:
function ilkAny(dizi: any[]): any {
return dizi[0];
}
function ilk<T>(dizi: T[]): T | undefined {
return dizi[0];
}
const a = ilkAny([1, 2, 3]); // any
const b = ilk([1, 2, 3]); // number | undefined
const c = ilk<string>(["x", "y"]); // string | undefinedİki çağrı biçimi var. ilk<string>(...) yazarak tip argümanını açıkça verebilirsiniz; ama çoğu zaman buna gerek yoktur, çünkü derleyici argümana bakıp T için tipi kendisi çıkarır (type argument inference). Karmaşık çağrılarda çıkarım yetersiz kalırsa açık tip argümanı yazmak yine mümkündür.
Dönüş tipindeki | undefined bilinçli bir seçim: boş dizide dizi[0] gerçekten undefined döner. Generic yazmak, dönüş tipini dürüst tutma sorumluluğunu ortadan kaldırmaz.
Generic tip değişkeniyle çalışmak
Derleyici, generic bir parametreyi fonksiyon gövdesinde her tip olabilirmiş gibi ele alır. Bu yüzden T üzerinde varlığından emin olmadığı bir özelliğe erişmenize izin vermez:
function uzunluk<T>(deger: T): number {
return deger.length;
// TS2339: Property 'length' does not exist on type 'T'.
}Bu mantıklı bir hata: biri bu fonksiyona 42 verebilir ve sayıların length özelliği yoktur. Çözüm, T'nin ne tür değerler olabileceğini daraltmaktır.
Generic kısıtlar: extends
Kısıt, tip parametresine "en az şu özelliklere sahip olmalı" şartı koyar. T extends Uzunluklu yazdığınızda T yine istediğiniz tip olabilir, yeter ki length: number özelliğini taşısın:
interface Uzunluklu {
length: number;
}
function enUzun<T extends Uzunluklu>(a: T, b: T): T {
return a.length >= b.length ? a : b;
}
const d = enUzun([1, 2], [1, 2, 3]); // number[]
const m = enUzun("elma", "kiraz"); // "elma" | "kiraz"
enUzun(10, 20);
// TS2345: Argument of type 'number' is not assignable to parameter of type 'Uzunluklu'.Dizi ve string kabul edilir, çünkü ikisinin de length özelliği var. Sayı ise kısıtı karşılamadığı için çağrı derleme aşamasında reddedilir. Kısıtın bir başka faydası da gövdede a.length yazabilmenizdir; artık derleyici bu özelliğin var olduğunu bilir.
Bir tip parametresini diğerine bağlamak
Kısıt olarak başka bir tip parametresini de kullanabilirsiniz. En bilinen örnek, bir nesneden yalnızca gerçekten var olan bir anahtarı okumaya izin veren fonksiyondur. Burada K extends keyof T, K'nin T'nin anahtarlarından biri olması gerektiğini söyler; dönüş tipi T[K] ise o anahtarın değer tipidir:
function alanGetir<T, K extends keyof T>(nesne: T, anahtar: K): T[K] {
return nesne[anahtar];
}
const kullanici = { ad: "Ayşe", yas: 31, aktif: true };
const ad = alanGetir(kullanici, "ad"); // string
const yas = alanGetir(kullanici, "yas"); // number
alanGetir(kullanici, "eposta");
// TS2345: Argument of type '"eposta"' is not assignable to parameter of type '"ad" | "yas" | "aktif"'.keyof operatörünü ve T[K] biçimindeki indexed access tiplerini keyof, typeof ve indexed access yazımızda ayrıntılı anlatıyoruz.
Generic interface ve type alias
Tip parametreleri yalnızca fonksiyonlarda değil, tip tanımlarında da kullanılır. API yanıtları için tek bir kalıp yazıp içindeki veriyi değiştirmek tipik bir kullanım:
interface ApiYanit<T> {
basarili: boolean;
veri: T;
}
type Kutu<T> = { deger: T };
interface Urun {
id: number;
ad: string;
}
const yanit: ApiYanit<Urun[]> = {
basarili: true,
veri: [{ id: 1, ad: "Klavye" }],
};
const kutu: Kutu<number> = { deger: 5 };Generic bir interface'te tip parametresini nereye koyduğunuz önemlidir. Parametre çağrı imzasının üzerindeyse interface'in kendisi generic değildir, her çağrı kendi tipini seçer. Parametre interface adının yanındaysa, interface'i kullanırken tipi bir kez sabitlersiniz ve tüm üyeler o tiple çalışır:
// Her çağrı kendi T'sini seçer
interface KimlikFn {
<T>(x: T): T;
}
// T, interface kullanılırken bir kez sabitlenir
interface SayiKimlikFn<T> {
(x: T): T;
}
function kimlik<T>(x: T): T {
return x;
}
const f1: KimlikFn = kimlik;
const f2: SayiKimlikFn<number> = kimlik;
f1("metin"); // geçerli
f2(42); // geçerli; f2("metin") hata verirdiHandbook'a göre generic enum ve generic namespace yazılamaz; generic olabilen yapılar fonksiyonlar, interface'ler, type alias'lar ve sınıflardır.
Generic sınıflar
Sınıf adının hemen ardından gelen <T>, sınıfın tüm örnek (instance) üyelerinin aynı tiple çalışmasını sağlar. Basit bir yığın (stack) örneği:
class Yigin<T> {
#ogeler: T[] = [];
ekle(oge: T): void {
this.#ogeler.push(oge);
}
cikar(): T | undefined {
return this.#ogeler.pop();
}
get boyut(): number {
return this.#ogeler.length;
}
}
const y = new Yigin<number>();
y.ekle(1);
y.ekle(2);
console.log(y.cikar(), y.boyut); // 2 1
y.ekle("3");
// TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.Bir sınıfın tipinin iki yüzü vardır: statik taraf ve örnek tarafı. Generic sınıflar yalnızca örnek tarafında generic'tir; bu yüzden statik üyeler sınıfın tip parametresini kullanamaz:
class Onbellek<T> {
static varsayilan: T;
// TS2302: Static members cannot reference class type parameters.
}Sınıflardaki erişim belirleyicileri ve diğer özellikler için TypeScript class ve erişim belirleyiciler yazısına bakabilirsiniz.
Sınıfı parametre olarak almak
Fabrika fonksiyonlarında bir sınıfın kendisini (yapıcı fonksiyonunu) parametre olarak alırsınız. Bunun tipi new () => T biçiminde bir yapı imzasıdır:
function olustur<T>(sinif: new () => T): T {
return new sinif();
}
class Ayar {
tema = "koyu";
}
const ayar = olustur(Ayar); // Ayar
console.log(ayar.tema); // koyuVarsayılan tip parametreleri

Bir tip parametresine = Tip ile varsayılan değer verirseniz, o tip argümanını yazmak isteğe bağlı olur. Çıkarım yapılacak bir aday bulunamadığında da varsayılan tip kullanılır:
type Olay<T = string> = { ad: string; veri: T };
const o1: Olay = { ad: "giris", veri: "ok" }; // T = string
const o2: Olay<number> = { ad: "sayac", veri: 3 }; // T = number
function liste<T = number>(...ogeler: T[]): T[] {
return ogeler;
}
const bos = liste(); // number[]
const harfler = liste("a", "b"); // string[]Handbook varsayılan tip parametreleri için birkaç kural sayar. En sık karşılaşacaklarınız şunlar:
- Varsayılanı olan parametre isteğe bağlıdır; isteğe bağlı bir parametreden sonra zorunlu parametre gelemez.
- Varsayılan tip, varsa parametrenin kısıtını karşılamalıdır.
- Tip argümanı yazarken yalnızca zorunlu parametreleri vermeniz yeterlidir; verilmeyenler varsayılana çözülür.
- Birleşen (merge edilen) class veya interface bildirimleri mevcut bir parametreye varsayılan ekleyebilir ya da varsayılanı olan yeni bir parametre tanıtabilir.
type Hatali<T = string, U> = [T, U];
// TS2706: Required type parameters may not follow optional type parameters.
type Hatali2<T extends string = number> = T;
// TS2344: Type 'number' does not satisfy the constraint 'string'.TypeScript 6.0: method sözdiziminde daha iyi çıkarım
Generic çağrılarda TypeScript, parametre tipi yazılmamış fonksiyonları (bağlama duyarlı, "contextually sensitive" fonksiyonlar) çıkarım sırasında önce atlar, diğer argümanlardan tipi bulur ve sonra bu fonksiyonlara döner. 6.0 öncesinde method sözdizimiyle yazılan fonksiyonlar, gövdede this kullanılmasa bile örtük bir this parametresi taşıdıkları için bu sınıfa sokuluyordu ve özellik sırası değişince çıkarım bozulabiliyordu. TypeScript 6.0 sürüm notlarına göre artık this hiç kullanılmıyorsa fonksiyon bağlama duyarlı sayılmıyor. Aşağıdaki kod TypeScript 6.0 ile hatasız derlenir:
declare function cagir<T>(nesne: {
uret: (x: number) => T;
tuket: (y: T) => void;
}): void;
// tuket önce yazılmış olsa da y: number olarak çıkarılır
cagir({
tuket(y) {
console.log(y.toFixed());
},
uret(x: number) {
return x * 2;
},
});Variance açıklamaları (in / out)
TypeScript, generic bir tipin alt tip ilişkisini (covariance, contravariance) yapıdan otomatik çıkarır. Çok nadir durumlarda, özellikle bazı döngüsel tiplerde, bu ölçüm hatalı olabilir; bunun için tip parametresine out (kovaryant), in (kontravaryant) ya da in out yazılabilir. Handbook bu konuda çok net: açıklama yalnızca yapının zaten sahip olduğu varyansı yazmak için kullanılmalı, tip denetimini değiştirmek için kullanılmamalı. Günlük kodda neredeyse hiç ihtiyaç duymazsınız.
Sık yapılan hatalar

Kısıta uyan ama T olmayan bir değer döndürmek
Kısıt, T'nin en az o şekle sahip olduğunu söyler; T'nin tam olarak o şekil olduğunu söylemez. Bu yüzden kısıta uyan yeni bir nesne döndürmek hata verir:
function varsayilanla<T extends { sayfa: number }>(secenek: T): T {
return { sayfa: 1 };
// TS2322: Type '{ sayfa: number; }' is not assignable to type 'T'.
// '{ sayfa: number; }' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '{ sayfa: number; }'.
}Çağıran { sayfa: 2, boyut: 50 } gibi daha zengin bir tip geçirebilir; { sayfa: 1 } o tipi karşılamaz. Çözüm, ya gelen nesneyi koruyarak dönmek ({ ...secenek, sayfa: 1 }) ya da dönüş tipini T yerine somut bir tip yapmaktır.
Yalnızca bir kez kullanılan tip parametresi
Tip parametresi imzada yalnızca bir yerde geçiyorsa çoğu zaman gereksizdir: iki değer arasında bir ilişki kurmuyordur. TypeScript 5.4 sürüm notları da imzada tek kez kullanılan tip parametresini çoğu zaman bir "kod kokusu" olarak niteler. function yaz<T>(x: T): void yerine function yaz(x: unknown): void aynı işi görür. unknown tipini any, unknown ve never yazımızda anlatıyoruz.
Gereksiz açık tip argümanı
Çıkarımın doğru çalıştığı yerde ilk<number>([1, 2]) yazmak yalnızca gürültü ekler. Açık tip argümanını, derleyici istediğiniz tipi çıkaramadığında ya da bir literal tipin genişlemesini istemediğinizde kullanın.
Sık Sorulan Sorular
Tip parametresi adı neden hep T?
Bu yalnızca bir gelenektir. T, K (key), V (value) gibi tek harfler yaygındır; Handbook örneklerinde Type gibi uzun adlar da kullanılır. Derleyici için ad önemli değildir, okunabilirlik için anlamlı adlar seçebilirsiniz.
Generics çalışma zamanında var mı?
Hayır. Tip parametreleri derleme sırasında denetlenir ve üretilen JavaScript'ten tamamen silinir. Bu yüzden fonksiyon içinde T'nin hangi tip olduğunu çalışma anında sorgulayamazsınız; değer üzerinde narrowing ve type guard kullanmanız gerekir.
Generic mi, overload mu kullanmalıyım?
Girdi ile çıktı arasında doğrudan bir tip ilişkisi varsa generic daha sade sonuç verir. Girdinin tipine göre farklı biçimde sonuç dönüyorsanız overload ya da conditional types daha uygun olabilir. Overload yazımı için fonksiyon tipleri ve overload yazımıza bakın.
Hazır generic tipler var mı?
Evet. Partial<T>, Pick<T, K>, ReturnType<T> gibi yerleşik generic tipler TypeScript ile birlikte gelir. Tamamını utility types rehberinde tablo halinde bulabilirsiniz.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: TypeScript öğrenme rehberi · Utility types · keyof ve indexed access · Conditional types · TypeScript ileri seviye özeti · JavaScript fonksiyonları