← Kursa Dön
📄 Text · 30 min

Temel Tipler

Giriş — Tip Sistemiyle Tanışma

Bir önceki derste TypeScript'in ne olduğunu ve neden kullanıldığını öğrendik. Şimdi işin özüne geçiyoruz: tip sistemi. TypeScript'in tüm gücü, tiplerden gelir. Tipler, kodunuzun ne tür verilerle çalıştığını derleme zamanında tanımlar ve kontrol eder — hata yapmanıza fırsat vermeden.

Düşünün: bir mutfakta farklı çekmeceler var. Biri bıçaklar için, biri kaşıklar için, biri baharatlar için. Her şeyi tek çekmeceye atarsanız, bıçak ararken elinizi baharata sokarsınız. Tip sistemi, kodunuzdaki her çekmeceye etiket yapıştırmaktır — neyin nereye konacağını ve nereden alınacağını herkes bilir.

Analoji: Bir kargo şirketinde paketlerin üzerine etiket konur: "Kırılacak", "Soğuk Zincir", "Ağır Yük", "Belge". Bu etiketler sayesinde kırılacak paket üzerine ağır yük konmaz, soğuk zincir paketi güneşte bırakılmaz. TypeScript tipleri de aynı işlevi görür — her verinin üzerinde ne olduğunu yazan bir etiket taşır ve yanlış kullanımı daha kargo gönderilmeden (kod çalışmadan) yakalar.


Temel (Primitive) Tipler

TypeScript, JavaScript'in primitive tiplerinin her birini destekler ve üzerine ek özellikler ekler.

string

// String tipi — metin verileri
let isim: string = "Ahmet";
let soyisim: string = 'Yılmaz';
let mesaj: string = `Merhaba, ${isim} ${soyisim}!`; // Template literal

// Tip çıkarımı — tip belirtmeye gerek yok
let sehir = "İstanbul"; // TypeScript otomatik string çıkarır

// ❌ Yanlış tip ataması
// isim = 42;           // Error: Type 'number' is not assignable to type 'string'
// isim = true;         // Error: Type 'boolean' is not assignable to type 'string'
// isim = null;         // Error (strict mode): Type 'null' is not assignable to type 'string'

number

// Number tipi — tamsayı ve ondalık ayrımı YOK (JavaScript gibi)
let yas: number = 28;
let fiyat: number = 99.99;
let negatif: number = -42;

// Özel değerler de number tipindedir
let sonsuz: number = Infinity;
let sayiDegil: number = NaN;

// Farklı sayı gösterimleri
let hex: number = 0xFF;       // 255
let binary: number = 0b1010;  // 10
let octal: number = 0o744;    // 484

// BigInt — çok büyük tam sayılar için (ayrı bir tip!)
let buyukSayi: bigint = 9007199254740991n;
let digerBuyuk: bigint = BigInt("123456789012345678901234567890");

// ⚠️ number ve bigint birbirine atanamaz
// let x: number = buyukSayi;  // Error!
// let y: bigint = 42;         // Error! 42n olmalı

boolean

let aktif: boolean = true;
let silindi: boolean = false;

// Boolean genellikle koşullardan gelir — tip çıkarımı çalışır
let yetiskin = yas >= 18;       // boolean çıkarılır
let gecerli = isim.length > 0;  // boolean çıkarılır

// ⚠️ Truthy/Falsy JavaScript'te çalışır ama TypeScript tip olarak ayırmaz
// TypeScript'te boolean sadece true/false — 0, "", null "boolean" değildir

null ve undefined

Bu iki tip, strictNullChecks: true ile (ki varsayılan strict modda açıktır) özel davranır:

// strictNullChecks: true (önerilen ayar)
let x: string = "merhaba";
// x = null;       // ❌ Error: Type 'null' is not assignable to type 'string'
// x = undefined;  // ❌ Error: Type 'undefined' is not assignable to type 'string'

// Null olabilen tip — union ile belirt
let kullaniciAdi: string | null = "Ahmet";
kullaniciAdi = null;     // ✅ İzin verildi
// kullaniciAdi = undefined; // ❌ Hâlâ izin verilmez — sadece null dedik

// undefined olabilen tip
let deger: number | undefined = 42;
deger = undefined;       // ✅

// Opsiyonel parametre — otomatik olarak | undefined ekler
function selamla(isim?: string) {
  // isim: string | undefined
  if (isim) {
    console.log(`Merhaba, ${isim}!`);
  } else {
    console.log("Merhaba!");
  }
}

selamla("Ali");   // "Merhaba, Ali!"
selamla();        // "Merhaba!"

💡 İpucu: strictNullChecks: true ile çalışmak başlangıçta zor gelebilir — her yerde null kontrolü yapmanız gerekir. Ama bu, JavaScript'in en yaygın hata kaynağı olan "Cannot read properties of null" hatasını derleme zamanında yakalar. Kısa vadeli rahatsızlık, uzun vadeli güvenlik sağlar.

symbol

// Symbol — benzersiz tanımlayıcılar
const id: symbol = Symbol("kullanici-id");
const digerID: symbol = Symbol("kullanici-id");

console.log(id === digerID); // false — her Symbol benzersiz

// unique symbol — daha katı tip
const OZEL_ANAHTAR: unique symbol = Symbol("ozel");
// const baska: unique symbol = OZEL_ANAHTAR; // Error: unique symbol sadece const'a atanabilir

Array (Dizi) Tipi

TypeScript'te diziler, içerdikleri elemanların tipini belirtir:

// İki sözdizimi — ikisi de aynı
let sayilar: number[] = [1, 2, 3, 4, 5];
let isimler: Array<string> = ["Ali", "Ayşe", "Mehmet"];

// Tip çıkarımı
let meyveler = ["elma", "armut", "portakal"]; // string[] çıkarılır
let notlar = [85, 92, 78];                     // number[] çıkarılır

// ❌ Yanlış tip elemanı
// sayilar.push("altı");  // Error: Argument of type 'string' is not assignable to parameter of type 'number'
// isimler[0] = 42;       // Error!

// Boş dizi — dikkat!
let bos = [];             // any[] çıkarılır (⚠️ kötü!)
let bosDoğru: string[] = []; // ✅ Tipi belirt

// Birden fazla tipte dizi — union kullan
let karisik: (string | number)[] = ["Ahmet", 28, "İstanbul"];
karisik.push("yeni");   // ✅
karisik.push(42);       // ✅
// karisik.push(true);  // ❌ boolean izin verilmedi

// Readonly dizi — değiştirilemez
const sabitler: readonly number[] = [1, 2, 3];
// sabitler.push(4);     // ❌ Error: Property 'push' does not exist on type 'readonly number[]'
// sabitler[0] = 99;     // ❌ Error: Index signature in type 'readonly number[]' only permits reading

// ReadonlyArray<T> ile aynı
const renkler: ReadonlyArray<string> = ["kırmızı", "mavi", "yeşil"];

Tuple (Demet)

Tuple, sabit uzunlukta ve her pozisyonda belirli tipte eleman içeren özel bir dizi türüdür. Normal diziler tek bir tip belirtir ve herhangi bir uzunlukta olabilirken, tuple her pozisyon için ayrı tip belirtir.

Analoji: Normal dizi, aynı tip meyvelerin konulduğu bir kasa gibidir — hep elma veya hep portakal. Tuple ise sushi tabağı gibidir — birinci pozisyonda somon, ikincide ton, üçüncüde avokado olacak diye bellidir. Sıra ve tip sabittir.

// Tuple tanımlama — [tip1, tip2, ...]
let kullanici: [string, number] = ["Ahmet", 28];

// Her pozisyonun tipi belirli
console.log(kullanici[0].toUpperCase()); // ✅ string metodları
console.log(kullanici[1].toFixed(2));    // ✅ number metodları

// ❌ Yanlış sıra veya tip
// kullanici = [28, "Ahmet"];          // Error: Type 'number' is not assignable to type 'string'
// kullanici = ["Ahmet"];              // Error: Tuple has 2 elements but only 1 was provided
// kullanici = ["Ahmet", 28, "fazla"]; // Error: Tuple has 2 elements but 3 were provided

// Tuple destructuring — çok doğal çalışır
const [isim, yas] = kullanici;
console.log(isim); // "Ahmet" — tip: string
console.log(yas);  // 28 — tip: number

// Fonksiyon dönüş tipi olarak tuple
function koordinatAl(): [number, number] {
  return [41.0082, 28.9784]; // İstanbul
}

const [lat, lng] = koordinatAl();
console.log(`Enlem: ${lat}, Boylam: ${lng}`);

// Opsiyonel eleman
let bilgi: [string, number, boolean?] = ["Ali", 25];     // ✅
let bilgi2: [string, number, boolean?] = ["Ali", 25, true]; // ✅

// Etiketli (labeled) tuple — okunabilirlik için (TypeScript 4.0+)
type Koordinat = [enlem: number, boylam: number, yukseklik?: number];
const nokta: Koordinat = [41.0082, 28.9784];
const dagNokta: Koordinat = [39.7, 44.3, 5137]; // Ağrı Dağı

// Rest element ile tuple
type VeriSatiri = [number, string, ...boolean[]];
const satir1: VeriSatiri = [1, "Ali", true, false, true];
const satir2: VeriSatiri = [2, "Ayşe"]; // ✅ rest boş olabilir

// Readonly tuple
const sabitKoordinat: readonly [number, number] = [41.0, 29.0];
// sabitKoordinat[0] = 42; // ❌ Error: Cannot assign to '0' because it is a read-only property

Ne Zaman Tuple Kullanılır?

// 1. Fonksiyon birden fazla değer döndürdüğünde
function minMax(dizi: number[]): [number, number] {
  return [Math.min(...dizi), Math.max(...dizi)];
}
const [min, max] = minMax([5, 3, 8, 1, 9]);

// 2. Key-value çiftleri
const girisler: [string, number][] = [
  ["Ali", 85],
  ["Ayşe", 92],
  ["Mehmet", 78]
];
// Map constructor'ına da verilebilir
const notHaritasi = new Map(girisler);

// 3. React'taki useState gibi kalıplar
function useCounter(baslangic: number): [number, () => void, () => void] {
  let deger = baslangic;
  return [
    deger,
    () => { deger++; },
    () => { deger--; }
  ];
}

⚠️ Dikkat: Tuple'lar çalışma zamanında normal JavaScript dizisine dönüşür — TypeScript'in tip korumalarını yalnızca derleme zamanında sağlar. push() gibi metotlarla tuple'a eleman ekleyebilirsiniz ve TypeScript bunu yakalayamaz:

const tuple: [string, number] = ["Ali", 28];
tuple.push(true); // ⚠️ TypeScript HATA VERMEZ ama tuple yapısı bozulur
// Bu, TypeScript'in bilinen bir sınırlamasıdır

Enum (Numaralandırma)

Enum, ilişkili sabitleri bir grup altında toplar. Magic number'ları ve string'leri anlamlı isimlerle değiştirerek kodu daha okunabilir yapar.

Analoji: Trafik ışığını düşünün. Kırmızı=0, Sarı=1, Yeşil=2 diye sayılarla ifade edebilirsiniz. Ama kod içinde if (isik === 0) yazmak yerine if (isik === TrafikIsigi.Kirmizi) yazmak çok daha anlaşılırdır. Enum, budur.

Numeric Enum

// Varsayılan: 0'dan başlayıp otomatik artar
enum Yon {
  Kuzey,  // 0
  Guney,  // 1
  Dogu,   // 2
  Bati    // 3
}

let pusula: Yon = Yon.Kuzey;
console.log(pusula);      // 0
console.log(Yon.Dogu);    // 2
console.log(Yon[2]);      // "Dogu" (reverse mapping — sadece numeric enum'da)

// Başlangıç değeri belirtme
enum HttpDurum {
  OK = 200,
  OlusturulDu = 201,
  Yonlendirme = 301,
  Bulunamadi = 404,
  SunucuHatasi = 500
}

function yanıtKontrol(durum: HttpDurum) {
  if (durum === HttpDurum.OK) {
    console.log("İstek başarılı");
  } else if (durum === HttpDurum.Bulunamadi) {
    console.log("Sayfa bulunamadı");
  }
}

yanıtKontrol(HttpDurum.OK);        // ✅
yanıtKontrol(200);                  // ✅ (numeric enum sayı da kabul eder)
// yanıtKontrol(999);               // ⚠️ TypeScript buna da izin verir!

String Enum

// String enum — daha güvenli ve debug dostu
enum Rol {
  Admin = "ADMIN",
  Moderator = "MODERATOR",
  Kullanici = "KULLANICI",
  Misafir = "MISAFIR"
}

function yetkiKontrol(rol: Rol): boolean {
  return rol === Rol.Admin || rol === Rol.Moderator;
}

yetkiKontrol(Rol.Admin);      // ✅ true
// yetkiKontrol("ADMIN");     // ❌ Error! String enum'da sadece enum değeri kabul edilir
// Bu, numeric enum'dan DAHA GÜVENLİDİR

// JSON'da kullanım
const kullanici = {
  isim: "Ali",
  rol: Rol.Admin // "ADMIN" olarak serialize edilir
};
console.log(JSON.stringify(kullanici));
// {"isim":"Ali","rol":"ADMIN"}

const enum — Performans Optimizasyonu

// const enum derleme zamanında tamamen SİLİNİR (inline edilir)
const enum Renk {
  Kirmizi = "RED",
  Mavi = "BLUE",
  Yesil = "GREEN"
}

let arkaplan = Renk.Kirmizi;
// Derlenen JavaScript: let arkaplan = "RED";
// Enum objesi oluşturulmaz — sıfır runtime maliyeti

Enum vs Union Type — Modern Tercih

// Enum ile
enum Durum {
  Aktif = "AKTIF",
  Pasif = "PASIF",
  Beklemede = "BEKLEMEDE"
}

// Union type ile (modern tercih — birçok durumda daha iyi)
type DurumUnion = "AKTIF" | "PASIF" | "BEKLEMEDE";

function durumDegistir(yeniDurum: DurumUnion) {
  console.log(`Durum: ${yeniDurum}`);
}

durumDegistir("AKTIF");       // ✅
// durumDegistir("SILINDI");  // ❌ Error!

// Union type avantajları:
// ✅ Derlenen JavaScript'te iz bırakmaz (sıfır runtime maliyeti)
// ✅ String literal birleşimlerle doğal çalışır
// ✅ Tree-shaking dostu

// Enum avantajları:
// ✅ Reverse mapping (numeric enum)
// ✅ Namespace (Rol.Admin gibi gruplandırma)
// ✅ IDE'de otomatik tamamlama

💡 İpucu: Modern TypeScript projelerinde, basit string sabitler için union type tercih edin. Enum'ı, gruplama ve organizasyonun önemli olduğu, numeric değerlerin gerektiği veya büyük sabit kümeleri için kullanın.


any vs unknown — Bilinmeyen Verilerle Çalışma

TypeScript'te "her tipi kabul eden" iki tip vardır: any ve unknown. Aralarındaki fark güvenlik açısından çok kritiktir.

any — Tip Kontrolünü Kapat

// any → "Ne olursa olsun, kontrol etme"
let hersey: any = "merhaba";
hersey = 42;
hersey = true;
hersey = { x: 1 };

// ⚠️ any ile HİÇBİR kontrol yapılmaz
hersey.olmayanMetot();     // Hata YOK — derleme geçer, runtime'da patlar!
hersey.x.y.z;              // Hata YOK — derleme geçer
hersey();                  // Hata YOK — fonksiyon olmasa bile

// any bulaşıcıdır — dokunduğu her şeyi any yapar
let sayi: number = hersey; // ⚠️ Hata yok! Tip güvenliği sıfır
sayi.toUpperCase();        // ⚠️ Derleme hatası yok, runtime'da TypeError!

// any kullanmanız gereken TEK DURUM:
// JavaScript'ten TypeScript'e geçiş sürecinde, GEÇICI olarak

unknown — Güvenli "Bilinmeyen"

// unknown → "Ne olduğunu BİLMİYORUM, kontrol edince kullan"
let bilinmeyen: unknown = "merhaba";
bilinmeyen = 42;
bilinmeyen = true;
bilinmeyen = { x: 1 };

// ❌ unknown ile DOĞRUDAN işlem yapılamaz
// bilinmeyen.toUpperCase();  // Error: Object is of type 'unknown'
// bilinmeyen.x;              // Error
// bilinmeyen();              // Error
// let sayi: number = bilinmeyen; // Error: Type 'unknown' is not assignable to type 'number'

// ✅ Tip kontrolü (narrowing) yapıldıktan sonra güvenle kullan
if (typeof bilinmeyen === "string") {
  console.log(bilinmeyen.toUpperCase()); // ✅ TypeScript artık string olduğunu bilir
}

if (typeof bilinmeyen === "number") {
  console.log(bilinmeyen.toFixed(2)); // ✅ number olduğunu bilir
}

// Gerçek dünya: API'den gelen veri
async function veriCek(url: string): Promise<unknown> {
  const yanit = await fetch(url);
  return yanit.json(); // Ne döneceği belli değil
}

const data = await veriCek("/api/kullanicilar");

// ❌ Doğrudan kullanamaz
// console.log(data.length);

// ✅ Tip kontrolü ile güvenle kullan
if (Array.isArray(data)) {
  console.log(`${data.length} kayıt bulundu`);
}

any vs unknown Karşılaştırma

Özellikanyunknown
Her şey atanabilir
Başka tipe atanabilir✅ (tehlikeli!)❌ (önce kontrol et)
Metot çağrılabilir✅ (tehlikeli!)❌ (önce kontrol et)
Property'ye erişilebilir✅ (tehlikeli!)❌ (önce kontrol et)
Güvenli mi?
KullanımGeçiş dönemi, escape hatchAPI yanıtları, bilinmeyen veri

Altın kural: any yerine her zaman unknown kullanmaya çalışın. any, TypeScript'in gücünü sıfırlar.


void ve never

void — Dönüş Değeri Yok

// void → fonksiyon bir şey döndürmüyor
function logYaz(mesaj: string): void {
  console.log(mesaj);
  // return yok — veya return; (değersiz)
}

// undefined'dan farkı:
// void → "dönüş değerini KULLANMA" demek
// undefined → gerçekten undefined değeri döndürüyor

// Callback'lerde void önemli:
type Callback = (mesaj: string) => void;

const fn: Callback = (mesaj) => {
  console.log(mesaj);
  return 42; // ⚠️ Hata YOK! void callback'te return değeri yok sayılır
};

// Ama dönen değer kullanılamaz
// const sonuc: number = fn("test"); // Error: Type 'void' is not assignable to type 'number'

never — Asla Olmaz

// never → fonksiyon ASLA normal şekilde bitmez
function hataFirlat(mesaj: string): never {
  throw new Error(mesaj);
  // Bu noktadan sonra kod ÇALIŞMAZ — never
}

function sonsuzDongu(): never {
  while (true) {
    // Sonsuza kadar çalışır — asla return olmaz
  }
}

// never'ın gerçek gücü: exhaustiveness checking
type Sekil = "daire" | "kare" | "ucgen";

function alanHesapla(sekil: Sekil): number {
  switch (sekil) {
    case "daire":
      return Math.PI * 10 * 10;
    case "kare":
      return 10 * 10;
    case "ucgen":
      return (10 * 8) / 2;
    default:
      // Buraya ulaşılmamalı — tüm durumlar handle edildi
      const _kontrol: never = sekil;
      throw new Error(`Bilinmeyen şekil: ${_kontrol}`);
  }
}

// Eğer Sekil'e "dikdortgen" eklerseniz ve switch'te handle etmezseniz:
// Error: Type '"dikdortgen"' is not assignable to type 'never'
// TypeScript sizi uyarır — bir durumu unuttunuz!

💡 İpucu: never ile exhaustiveness checking, union type'lara yeni değer eklendiğinde tüm switch/if yapılarının güncellenmesini garanti eder. Büyük projelerde, bir enum veya union'a değer ekleyip bir yerde handle etmeyi unutmak çok yaygın bir hata kaynağıdır — never bunu önler.


Union Types (Birleşim Tipleri)

Union type, bir değerin birden fazla tipten biri olabileceğini belirtir. | (pipe) operatörü ile tanımlanır.

// Temel union
let kimlikNo: string | number;
kimlikNo = "12345678901"; // ✅ TC kimlik — string
kimlikNo = 123456;         // ✅ Numara — number
// kimlikNo = true;        // ❌ boolean izin verilmez

// Fonksiyon parametresinde
function kimlikGoster(id: string | number): string {
  // ⚠️ Doğrudan string veya number metotları çağıramazsınız
  // id.toUpperCase(); // Error: number'da toUpperCase yok

  // ✅ Narrowing (daraltma) ile
  if (typeof id === "string") {
    return `TC: ${id.toUpperCase()}`; // string metodları güvenli
  } else {
    return `No: ${id.toFixed(0)}`;    // number metodları güvenli
  }
}

// String literal union — belirli değerlerle kısıtlama
type Yonelim = "kuzey" | "guney" | "dogu" | "bati";

function yonBelirle(yon: Yonelim): void {
  console.log(`Yön: ${yon}`);
}

yonBelirle("kuzey"); // ✅
// yonBelirle("yukari"); // ❌ Error: '"yukari"' is not assignable to type 'Yonelim'

// Number literal union
type ZarDegeri = 1 | 2 | 3 | 4 | 5 | 6;
function zarAt(): ZarDegeri {
  return Math.ceil(Math.random() * 6) as ZarDegeri;
}

// Null olabilen tipler — union'ın en yaygın kullanımı
type Sonuc = string | null;

function kullaniciBul(id: number): Sonuc {
  const kullanicilar = new Map([[1, "Ali"], [2, "Ayşe"]]);
  return kullanicilar.get(id) ?? null;
}

const kullanici = kullaniciBul(3); // string | null
if (kullanici !== null) {
  console.log(kullanici.toUpperCase()); // ✅ null kontrolü yapıldı
}

Intersection Types (Kesişim Tipleri)

Intersection type, birden fazla tipi birleştirerek tüm özellikleri taşıyan yeni bir tip oluşturur. & operatörü ile tanımlanır.

Analoji: Union, "A veya B" demek. Intersection, "A ve B" demek. Bir kişi hem öğrenci hem çalışan olabilir — intersection, bu iki rolün tüm özelliklerini birleştirir.

// Temel intersection
type Isim = {
  ad: string;
  soyad: string;
};

type Iletisim = {
  email: string;
  telefon: string;
};

// İki tipin birleşimi — TÜM özellikleri taşımalı
type Kisi = Isim & Iletisim;

const ali: Kisi = {
  ad: "Ali",
  soyad: "Yılmaz",
  email: "ali@example.com",
  telefon: "555-1234"
};

// ❌ Eksik özellik → hata
// const eksik: Kisi = {
//   ad: "Ali",
//   soyad: "Yılmaz"
//   // email ve telefon eksik!
// };

// Pratik: API yanıtını genişletme
type ApiYanit = {
  basarili: boolean;
  zamanDamgasi: number;
};

type KullaniciYaniti = ApiYanit & {
  veri: {
    id: number;
    isim: string;
    email: string;
  };
};

type HataYaniti = ApiYanit & {
  hata: {
    kod: number;
    mesaj: string;
  };
};

function yanıtIsle(yanit: KullaniciYaniti | HataYaniti) {
  console.log(`Başarılı: ${yanit.basarili}`); // ✅ Ortak özellik
  console.log(`Zaman: ${yanit.zamanDamgasi}`);

  if (yanit.basarili) {
    const kYanit = yanit as KullaniciYaniti;
    console.log(`Kullanıcı: ${kYanit.veri.isim}`);
  } else {
    const hYanit = yanit as HataYaniti;
    console.log(`Hata: ${hYanit.hata.mesaj}`);
  }
}

Union vs Intersection — Hangi Durumda?

// Union (|) → "A veya B olabilir" — SEÇENEK
type Girdi = string | number;
// Girdi, string VEYA number olabilir

// Intersection (&) → "Hem A hem B olmalı" — BİRLEŞTİRME
type Tam = Isim & Iletisim;
// Tam, Isim'in TÜM özelliklerini VE Iletisim'in TÜM özelliklerini taşımalı

// Union genellikle "olası değerler" için:
type Durum = "yukleniyor" | "basarili" | "hatali";

// Intersection genellikle "nesne birleştirme" için:
type LogluKullanici = Kullanici & { sonGiris: Date; logSayisi: number };

Tip Daraltma (Type Narrowing)

Union type'larla çalışırken, TypeScript'in bir değerin gerçek tipini anlamasını sağlamak önemlidir. Bu sürece narrowing (daraltma) denir.

// typeof ile narrowing
function islemYap(deger: string | number | boolean): string {
  if (typeof deger === "string") {
    return deger.toUpperCase();        // TypeScript: string
  } else if (typeof deger === "number") {
    return deger.toFixed(2);           // TypeScript: number
  } else {
    return deger ? "Evet" : "Hayır";   // TypeScript: boolean
  }
}

// Truthiness narrowing
function karsilamaYaz(isim: string | null | undefined): string {
  if (isim) {
    return `Merhaba, ${isim}!`;  // TypeScript: string (null/undefined elendi)
  }
  return "Merhaba, misafir!";
}

// in operatörü ile narrowing
type Kedi = { miyavla: () => void; tirmanla: () => void };
type Kopek = { havla: () => void; kazSin: () => void };

function sesCikar(hayvan: Kedi | Kopek) {
  if ("miyavla" in hayvan) {
    hayvan.miyavla();    // TypeScript: Kedi
  } else {
    hayvan.havla();      // TypeScript: Kopek
  }
}

// instanceof ile narrowing
function uzunlukAl(deger: string | string[]): number {
  if (deger instanceof Array) {
    return deger.length; // TypeScript: string[]
  }
  return deger.length;   // TypeScript: string
}

Tip İddiaları (Type Assertions)

Bazen siz TypeScript'ten daha fazla bilgiye sahipsinizdir. Tip iddiaları (assertions), TypeScript'e "ben ne yaptığımı biliyorum" demenin yoludur.

// as sözdizimi (önerilen)
const girdi = document.querySelector("#email") as HTMLInputElement;
console.log(girdi.value); // ✅ HTMLInputElement olarak davranır

// Açılı parantez sözdizimi (JSX ile çakışır — kullanmayın)
const girdi2 = <HTMLInputElement>document.querySelector("#email");

// Dikkatli kullanın — yanlış assertion runtime hatası verir
const sayi = "merhaba" as unknown as number;
console.log(sayi.toFixed(2)); // 💥 Runtime hatası! String'de toFixed yok

// Non-null assertion (!) — "bu kesinlikle null değil"
const element = document.querySelector("#baslik")!; // null olmadığına eminim
element.textContent = "Yeni başlık";

// ⚠️ ! operatörü null kontrolünü ATLAR — element yoksa runtime hatası!
// Daha güvenli alternatif:
const guvenli = document.querySelector("#baslik");
if (guvenli) {
  guvenli.textContent = "Yeni başlık"; // ✅ Null kontrolü ile
}

⚠️ Dikkat: Type assertions, TypeScript'in tip kontrolünü atlatır. Yanlış kullanımda runtime hataları ortaya çıkar. Mümkün olduğunca narrowing (typeof, instanceof, in) tercih edin, assertion'ı son çare olarak kullanın.


Gerçek Dünya Örneği: API Yanıt İşleme

// API yanıt tipleri
type ApiBasarili<T> = {
  basarili: true;
  veri: T;
  zamanDamgasi: number;
};

type ApiHata = {
  basarili: false;
  hata: {
    kod: number;
    mesaj: string;
    detay?: string;
  };
  zamanDamgasi: number;
};

type ApiYanit<T> = ApiBasarili<T> | ApiHata;

// Kullanıcı tipi
type Kullanici = {
  id: number;
  isim: string;
  email: string;
  rol: "admin" | "kullanici" | "moderator";
  aktif: boolean;
};

// API fonksiyonu
async function kullanicilariGetir(): Promise<ApiYanit<Kullanici[]>> {
  try {
    const yanit = await fetch("/api/kullanicilar");
    const veri: unknown = await yanit.json();

    if (!yanit.ok) {
      return {
        basarili: false,
        hata: { kod: yanit.status, mesaj: "İstek başarısız" },
        zamanDamgasi: Date.now()
      };
    }

    return {
      basarili: true,
      veri: veri as Kullanici[],
      zamanDamgasi: Date.now()
    };
  } catch (err) {
    return {
      basarili: false,
      hata: { kod: 0, mesaj: err instanceof Error ? err.message : "Bilinmeyen hata" },
      zamanDamgasi: Date.now()
    };
  }
}

// Kullanım — tip daraltma ile güvenli erişim
async function main() {
  const yanit = await kullanicilariGetir();

  if (yanit.basarili) {
    // TypeScript biliyor: yanit.veri → Kullanici[]
    const adminler = yanit.veri.filter(k => k.rol === "admin");
    console.log(`${adminler.length} admin bulundu`);

    yanit.veri.forEach(k => {
      console.log(`${k.isim} (${k.email}) — ${k.aktif ? "aktif" : "pasif"}`);
    });
  } else {
    // TypeScript biliyor: yanit.hata → { kod, mesaj, detay? }
    console.error(`Hata ${yanit.hata.kod}: ${yanit.hata.mesaj}`);
  }
}

Özet

  • 🔹 TypeScript'in temel tipleri: string, number, boolean, null, undefined, symbol, bigint — JavaScript primitive'lerinin tip-güvenli karşılıkları.

  • 🔹 Tuple ([string, number]) sabit uzunluk ve pozisyon bazlı tipleme sağlar. Çoklu dönüş değerleri ve key-value çiftleri için idealdir.

  • 🔹 Enum ilişkili sabitleri gruplar. String enum'lar daha güvenli; basit durumlar için union type ("a" | "b") modern tercihtir.

  • 🔹 `any` tip kontrolünü tamamen devre dışı bırakır — kaçının. `unknown` güvenli alternatiftir: kullanmadan önce tip kontrolü zorunludur.

  • 🔹 Union (A | B) "A veya B olabilir", Intersection (A & B) "hem A hem B olmalı" anlamına gelir. Narrowing (typeof, in, instanceof) ile union tipleri güvenle daraltın.

  • 🔹 `void` dönüş değeri olmayan fonksiyonlar, `never` asla tamamlanmayan fonksiyonlar ve exhaustiveness checking için kullanılır.