TypeScript'te union tipi (A | B) bir değerin birkaç tipten herhangi biri olabileceğini, intersection tipi (A & B) ise değerin birkaç tipin hepsini aynı anda karşılaması gerektiğini söyler. Union ile çalışırken yalnızca tüm üyelerde geçerli olan işlemleri yapabilirsiniz; daha fazlası için tipi daraltırsınız. Ortak bir literal alanla (kind, type, ok gibi) ayırt edilen union'lara discriminated union denir ve TypeScript bu alanı kontrol ettiğinizde doğru üyeyi kendisi seçer.
Bu yazıda union ve intersection'ı tanımlamaktan başlayıp, birlikte kullanıldıklarında ortaya çıkan tuzaklara ve discriminated union tasarımına kadar ilerliyoruz. Örnekler TypeScript 6.0.3 ile tsc --strict --target es2022 --moduleDetection force seçenekleriyle derlendi; hata kodları ve mesajları bu derleyicinin gerçek çıktısıdır. Daraltma tekniklerinin tamamı için narrowing ve type guard yazısına bakabilirsiniz.
Union tipi: birkaç tipten biri

Union, iki ya da daha fazla tipin arasına | koyarak yazılır ve her birine union'ın üyesi denir. Aşağıdaki fonksiyon hem sayı hem metin kimlik kabul eder, ama nesne kabul etmez:
function kimlikYaz(id: number | string) {
console.log("Kimlik: " + id);
}
kimlikYaz(101); // geçerli
kimlikYaz("202"); // geçerli
kimlikYaz({ id: 22342 });
// TS2345: Argument of type '{ id: number; }' is not assignable to parameter of type 'string | number'.Uzun union'ları alt alta yazarken ilk üyenin önüne de | koyabilirsiniz; bu yalnızca biçimle ilgilidir ve anlamı değiştirmez. Sık kullandığınız bir union'a type ile ad vermek okunabilirliği artırır; bunun neden interface ile yapılamadığını interface ve type farkı yazısında anlattık.
Union ile çalışmak: yalnızca ortak işlemler serbest
Union tipinde bir değer vermek kolaydır, üyelerden birine uyan herhangi bir değer yeterlidir. Asıl soru, elinizde union tipinde bir değer varken onunla ne yapabileceğinizdir. TypeScript yalnızca her üye için geçerli olan işlemlere izin verir:
function buyukYaz(id: number | string) {
return id.toUpperCase();
}
// TS2339: Property 'toUpperCase' does not exist on type 'string | number'.Çözüm, JavaScript'te zaten yazacağınız kontrolü yazmaktır. typeof id === "string" kontrolünden sonra derleyici o dalda id'nin string olduğunu bilir. Buna daraltma (narrowing) denir:
function buyukYaz(id: number | string) {
if (typeof id === "string") {
return id.toUpperCase(); // burada id: string
}
return id.toFixed(2); // burada id: number
}
console.log(buyukYaz("ab12")); // AB12
console.log(buyukYaz(7)); // 7.00Bazen üyelerin hepsinde aynı adlı bir özellik ya da metot bulunur. O zaman daraltmaya gerek kalmaz. Hem dizilerin hem string'lerin slice metodu olduğu için aşağıdaki kod derlenir ve dönüş tipi number[] | string olarak çıkarılır:
function ilkUc(x: number[] | string) {
return x.slice(0, 3);
}
console.log(ilkUc([5, 6, 7, 8])); // [ 5, 6, 7 ]
console.log(ilkUc("TypeScript")); // TypIntersection tipi: hepsi aynı anda

Intersection, & operatörüyle yazılır ve çoğunlukla var olan nesne tiplerini birleştirmek için kullanılır. Sonuç, tüm parçalardaki üyelerin hepsine sahip olan bir tiptir:
interface Renkli {
renk: string;
}
interface Daire {
yaricap: number;
}
function ciz(d: Renkli & Daire) {
console.log(`${d.renk} daire, yarıçap ${d.yaricap}`);
}
ciz({ renk: "mavi", yaricap: 42 }); // mavi daire, yarıçap 42Nesne literal'i doğrudan verildiğinde fazladan özellik denetimi de devreye girer. Yazım hatası yapılmış bir alan adı yakalanır ve derleyici doğru adı önerir:
interface Renkli {
renk: string;
}
interface Daire {
yaricap: number;
}
declare function ciz(d: Renkli & Daire): void;
ciz({ renk: "kırmızı", yaricapp: 42 });
// TS2561: Object literal may only specify known properties, but 'yaricapp' does not exist in type 'Renkli & Daire'. Did you mean to write 'yaricap'?Eski handbook bu yapının tipik bir kullanımını gösterir: Tüm ağ yanıtlarında ortak olan hata alanlarını ayrı bir tipte tutup her yanıt tipine intersection ile eklemek. Böylece hata yapısını tek yerde tanımlamış olursunuz.
Çakışan alanlar ve never
Intersection'daki parçalarda aynı adlı bir alan farklı tiplerle geçiyorsa TypeScript bunu bildirim anında hata saymaz. Alanın iki tipi birden karşılaması gerekir; string ve number olan bir değer olmadığından sonuç never olur:
type A = { kod: string };
type B = { kod: number };
type AB = A & B;
const x: AB = { kod: "42" };
// TS2322: Type 'string' is not assignable to type 'never'.
type Imkansiz = string & number; // neverInterface'te extends kullansaydınız aynı çakışma bildirimin olduğu satırda hata verirdi. Bu yüzden intersection ile kurulan tiplerde beklenmedik bir never gördüğünüzde ilk bakacağınız yer çakışan alan adlarıdır.
Discriminated union: ortak bir etiketle ayırt etmek
Handbook'taki klasik örnekte daire ve kare tek bir tipte, isteğe bağlı alanlarla modellenir. Bu tasarım, kind kontrol edilse bile derleyiciye yarıçap alanının dolu olduğunu söylemez:
interface Sekil {
kind: "daire" | "kare";
yaricap?: number;
kenar?: number;
}
function alan(s: Sekil) {
if (s.kind === "daire") {
return Math.PI * s.yaricap ** 2;
}
}
// TS18048: 's.yaricap' is possibly 'undefined'.Sorun tipin tasarımındadır: Derleyici kind ile yaricap arasında bir bağ olduğunu bilemez. Çözüm, her şekli kendi zorunlu alanlarıyla ayrı bir tip olarak yazıp, hepsinde ortak olan kind alanına farklı bir literal tip vermektir:
interface Daire {
kind: "daire";
yaricap: number;
}
interface Kare {
kind: "kare";
kenar: number;
}
type Sekil = Daire | Kare;
function alan(s: Sekil): number {
switch (s.kind) {
case "daire":
return Math.PI * s.yaricap ** 2; // s: Daire
case "kare":
return s.kenar ** 2; // s: Kare
}
}
console.log(alan({ kind: "kare", kenar: 3 })); // 9
console.log(alan({ kind: "daire", yaricap: 1 }).toFixed(4)); // 3.1416Union'ın her üyesi literal tipte ortak bir alan taşıdığında TypeScript bunu discriminated union olarak görür ve o alana bakan her kontrolde union'ı uygun üyeye daraltır. Buradaki kind alanına ayırt edici (discriminant) özellik denir. Aynı yapı yanlış yazılmış etiketleri de yakalar; s.kind === "dikdortgen" gibi bir karşılaştırma TS2367 hatası verir, çünkü iki tipin hiç ortak değeri yoktur.
Etiketin string olması gerekmez. Boolean literal'ler de ayırt edici olabilir ve sonuç döndüren fonksiyonlarda kullanışlıdır:
type Sonuc =
| { ok: true; deger: number }
| { ok: false; hata: string };
function bol(a: number, b: number): Sonuc {
if (b === 0) return { ok: false, hata: "sıfıra bölme" };
return { ok: true, deger: a / b };
}
const r = bol(10, 0);
if (r.ok) {
console.log(r.deger);
} else {
console.log("Hata:", r.hata); // Hata: sıfıra bölme
}TypeScript 4.4'ten beri kontrolü bir sabite taşımak da daraltmayı bozmaz. const daireMi = s.kind === "daire" yazıp if (daireMi) dediğinizde derleyici yine s'yi Daire'ye daraltır. Sürüm notlarına göre bu, kontrol edilen değer const, readonly bir özellik ya da değiştirilmemiş bir parametre olduğunda çalışır.
Tüm durumları ele aldınız mı?
Union'a yeni bir üye eklediğinizde onu işlemeyi unuttuğunuz yerleri bulmak istersiniz. En basit yol, fonksiyona açık bir dönüş tipi yazmaktır. switch tüm üyeleri kapsamıyorsa fonksiyonun bazen undefined döndürebileceği ortaya çıkar ve derleyici bunu bildirir:
type Durum =
| { durum: "yukleniyor" }
| { durum: "hata"; kod: number }
| { durum: "basarili" };
function etiket(d: Durum): string {
switch (d.durum) {
case "yukleniyor":
return "Yükleniyor";
case "hata":
return `Hata ${d.kod}`;
}
}
// TS2366: Function lacks ending return statement and return type does not include 'undefined'.Daha açık bir hata mesajı için never tipine dayanan kapsamlılık denetimi kullanılır; eksik üyenin adını doğrudan hata mesajında görürsünüz. Bu tekniği narrowing yazısındaki exhaustiveness bölümünde, never tipinin kendisini ise any, unknown ve never yazısında anlatıyoruz.
Union ve intersection karşılaştırması

| Konu | Union (A | B) | Intersection (A & B) |
|---|---|---|
| Anlamı | A ya da B | Hem A hem B |
| Değer verirken | Bir üyeye uymak yeter | Tüm parçalara uymak gerekir |
| Değeri kullanırken | Yalnızca ortak üyeler, sonra daraltma | Tüm parçaların üyeleri |
| Primitive örneği | string | number geniş bir tiptir | string & number never olur |
| Tipik kullanım | Seçenekler, durum makineleri, sonuç tipleri | Nesne tiplerini birleştirmek |
| unknown ile | unknown | T sonucu unknown | unknown & T sonucu T |
Tablonun son satırı TypeScript 3.0 sürüm notlarındaki kurallardan gelir: Union içinde unknown diğer üyeleri yutar, intersection içinde ise diğer tipler unknown'ı yutar.
Sık yapılan hatalar
- Union'da daraltmadan üyeye özgü metot çağırmak: TS2339 alırsınız. Önce
typeof,inya da ayırt edici alanla tipi daraltın. - Discriminated union yerine isteğe bağlı alanlarla tek tip yazmak: Derleyici alanların ne zaman dolu olduğunu bilemez ve sizi
!kullanmaya iter. Her durumu ayrı tip olarak yazın. - Ayırt edici alanı
stringyapmak: Etiket alanı genişstringolursa daraltma çalışmaz; her üyede farklı bir literal tip olmalıdır. - Intersection'da çakışan alanları gözden kaçırmak: Alan sessizce
neverolur ve hata uzak bir yerde çıkar. - Overload'a gereğinden çok başvurmak: Aynı dönüş tipine sahip birkaç overload yerine union parametreli tek imza çoğu zaman daha kullanışlıdır; ayrıntısı fonksiyon tipleri yazısında.
Sık Sorulan Sorular
Union ile enum arasında nasıl seçim yaparım?
String literal union'lar çalışma zamanında hiçbir kod üretmez; yalnızca tip düzeyindedir. Enum ise bir değer de oluşturur. Seçenekleri enum ve alternatifleri yazısında karşılaştırıyoruz.
Discriminated union için alanın adı önemli mi?
Hayır. kind, type, status ya da ok olabilir. Önemli olan alanın tüm üyelerde bulunması ve her üyede farklı bir literal tipe sahip olmasıdır.
Intersection ile extends aynı şey mi?
Sonuç çoğu zaman benzerdir, ama çakışmalarda davranış farklıdır: extends hatayı tanımda verir, intersection alanı never yapar.
API yanıtlarını nasıl tiplemeliyim?
Başarılı ve hatalı yanıtları ayrı üyeler olarak, ortak bir ayırt edici alanla yazmak en okunaklı yoldur. Gelen veriyi gerçekten doğrulamak ise çalışma zamanı işidir; tip yalnızca derleme sırasında yardımcı olur.
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 · Narrowing ve type guard · interface ve type farkı · Literal tipler ve as const · any, unknown, never ve void · TypeScript ileri seviye yazısı