İçeriğe geç

TypeScript Tip Sistemi ve Daraltma

Başlangıç 10 dk Sık karşılaşılır

30 saniyede özet

as yazmak derleyiciye 'bana güven, sus' demektir; tip daraltma ise ona bir kontrolle kanıt sunar. Ama tip doğru diye mantık da doğru olmaz: !count kontrolü sıfırı da eler.

Kapıdaki görevli kimliğine baktıktan sonra seni içeri alır. Baktıktan sonra artık kim olduğunu bilir ve sana ona göre davranır.

TypeScript de böyle: yaptığın her kontrolü takip eder ve tipi kendiliğinden daraltmaDerleyicinin, yazdığın kontrole bakarak bir değerin tipini daha dar bir tipe indirmesi. `if (!user) return;` sonrası `user` artık null olamaz.Sözlükte gör →.

  1. Bayt: Derleyici itiraz edince araya bir as yazdım, hata kayboldu!

  2. Sen: Sorun çözüldü mü yani?

  3. Bayt: Canlıda bir null geldi ve sayfa çöktü...

  4. Bayt: Kapıdaki görevli kimliğe bakmadan herkesi içeri alırsa ne olur?

Her kontrol bir olasılığı eler.
Adım adım oku
  1. Değer string, number ya da null olabilir; derleyici üçünü de hesaba katar.
  2. null kontrolünden sonra derleyici null ihtimalini eler.
  3. typeof ile string elenince geriye yalnızca number kalır ve cast gerekmez.
  4. as ise hiçbir şeyi kontrol etmez; yanlış değer de içeri girer ve program çalışırken çöker.
Cast yok, kanıt var
function format(value: string | number | null): string {
if (value === null) return "yok";
// value: string | number
if (typeof value === "string") return value.toUpperCase();
// value: number
return value.toFixed(2);
}

Hiçbir yerde as yok. Her kontrol birleşimden bir olasılık eledi.

Kendin izle

Simülatör satır satır ilerliyor; sağdaki panel derleyicinin o anda ne bildiğini gösteriyor.

TypeScript tip daraltma — derleyici satır satır ne biliyor

Tohum 1
  1. 1function format(value: string | number | null): string {
  2. 2 if (value === null) {
  3. 3 return "yok";
  4. 4 }
  5. 5
  6. 6 if (typeof value === "string") {
  7. 7 return value.toUpperCase();
  8. 8 }
  9. 9
  10. 10 return value.toFixed(2);
  11. 11}

Derleyicinin bildiği tipler

— henüz bir şey yok —

Hız
Adım 0

Şu an ne oldu?

Derleyici henüz bir şey bilmiyor

Oynat veya adımla. Her satırdan sonra derleyicinin o değişken hakkında ne bildiği sağda güncellenecek.

Aklında kalsın: Daraltma (narrowing), derleyicinin kontrol akışını takip ederek bir birleşim tipini küçültmesidir. Cast yazmanı gereksiz kılan şey budur.

Olay günlüğü (0)

Henüz olay yok. Oynat veya adımla.

Dört senaryoyu da çalıştır — son ikisi kasten bozuk kodu gösteriyor:

  • Union daraltma — tipin her kontrolde küçüldüğünü izle.
  • Ayrımlı birleşim — default dalında shape nasıl never oluyor.
  • any ile unknown — aynı satır, birinde derleme hatası, diğerinde çalışma zamanı çökmesi.
  • Truthy tuzağı — derlenen, tip kontrolünden geçen ve yine de yanlış olan kod.
Kafam karıştı, daha basit anlat

Daraltma, derleyiciye “bak, kontrol ettim” demektir. if ile bir şeyi kontrol ettiğinde, derleyici o bloğun içinde tipin ne olduğunu artık bilir.

Hızlı kontrolBaşlangıç

`interface` ile `type` arasındaki pratik fark nedir?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

as ile daraltmanın farkı

API'den gelen data aslında bir hata nesnesi. Kodda const user = data as User yazıyor. Derleyici bir uyarı verir mi? Cevabı göster

Hayır. as, derleyiciye “ben biliyorum” demektir ve hiçbir şey kontrol edilmez. Hata, user.email ilk okunduğunda çalışma anında çıkar.

İkisi aynı şey değil
// Cast: derleyiciye "sus, ben biliyorum" demek. Hiçbir şey doğrulanmaz.
const user = data as User;
// Daraltma: derleyiciye kanıt sunmak.
if (typeof data === "object" && data !== null && "email" in data) {
// data burada gerçekten o alana sahip
}

as bir çalışma zamanı kontrolü değildir — derlendikten sonra ortada hiçbir şey kalmaz. Yanlışsa program çöker.

Kafam karıştı, daha basit anlat

as derleyiciye “sus, ben biliyorum” demektir, hiçbir şey kontrol edilmez. Daraltma ise kanıt göstermektir. Kanıt her zaman susturmaktan güvenlidir.

Hızlı kontrolOrta

Bu kod ne yazdırır?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.
daraltma.ts
1function selamla(user: { ad: string } | null) {
2 if (!user) return "misafir";
3
4 const yazdir = () => user.ad;
5
6 return yazdir();
7}
8
9console.log(selamla({ ad: "Ayse" }));
10console.log(selamla(null));
TypeScriptUTF-8LF

Daraltmanın araçları

AraçNe için
typeof x === "string"İlkel tipler
x instanceof DateSınıflar
"email" in objAlan varlığı
x.kind === "circle"Ayrımlı birleşimler
Array.isArray(x)Diziler
x !== nullnull / undefined eleme
function isUser(x): x is UserKendi tip koruyucun

Son satır, kendi kontrolünü derleyiciye tanıtma yoludur:

Tip koruyucu (type guard)
function isUser(value: unknown): value is User {
return typeof value === "object" && value !== null && "email" in value;
}
if (isUser(data)) {
data.email; // derleyici artık biliyor
}

Dikkat: value is User bir söztür. Fonksiyonun gövdesi yanlış yazılmışsa derleyici seni yine yanıltır — tip koruyucusu`value is User` imzalı, true dönünce derleyiciye tipi daraltmasını söyleyen fonksiyon. Kontrolü sen yazarsın — gövde yanlışsa derleyici seni durduramaz.Sözlükte gör →lar as’a göre daha iyidir ama sihirli değildir.

Hızlı kontrolBaşlangıç

Her ifadeyi derleyiciye kanıt sunup sunmadığına göre yerleştir.

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Sınıflandırılmamış

Daraltma — kanıt sunar

Derleyici kontrol akışını takip eder

    İddia — kanıt sunmaz

    Derleyiciye susmasını söyler

      Eksik durum kalmasın: never

      Shape.ts
      type Shape =
      | { kind: "circle"; r: number }
      | { kind: "square"; side: number };
      function area(shape: Shape): number {
      switch (shape.kind) {
      case "circle":
      return Math.PI * shape.r ** 2;
      case "square":
      return shape.side ** 2;
      default:
      // Tüm varyantlar ele alındıysa shape burada `never` olur.
      const exhaustive: never = shape;
      return exhaustive;
      }
      }

      Union’a üçüncü bir varyant eklediğin an, o varyantı ele almayı unuttuğun her switch derleme hatası verir. Bu bir test değil, derleyici garantisidir — ve büyük kod tabanlarında TypeScript’in en değerli özelliklerinden biridir.

      Aynı fikir bir banka uygulamasında da işine yarar. Para gönderdikten sonra sunucu üç cevaptan birini döner; bak derleyici hiçbirini unutturmuyor.

      Derinleş · Havale sonucu: kanıtla daralt, hiçbir durumu unutma 4 dosya · ~72 satır · ilk okumada atlayabilirsin
      Proje dosyaları

      src/features/transfer/ transfer.ts Havale sonucu, status alanıyla ayrılan üç durumlu bir birleşim.

      src/features/transfer/transfer.ts
      export type RejectReason = 'INSUFFICIENT_FUNDS' | 'DAILY_LIMIT_EXCEEDED' | 'FRAUD_HOLD';
      // One status field decides which other fields exist.
      export type TransferResult =
      | { status: 'completed'; referenceNo: string; feeKurus: number }
      | { status: 'queued'; sendAt: string } // EFT outside working hours
      | { status: 'rejected'; reason: RejectReason };

      src/features/transfer/ parseTransferResult.ts Sunucudan gelen cevap unknown başlar ve alan alan kontrol edilerek daralır.

      src/features/transfer/parseTransferResult.ts
      import type { RejectReason, TransferResult } from './transfer';
      const REASONS: readonly RejectReason[] = ['INSUFFICIENT_FUNDS', 'DAILY_LIMIT_EXCEEDED', 'FRAUD_HOLD'];
      function isRejectReason(value: unknown): value is RejectReason {
      return REASONS.some((reason) => reason === value);
      }
      // The API response stays unknown until each field has been checked.
      export function parseTransferResult(body: unknown): TransferResult | null {
      if (typeof body !== 'object' || body === null || !('status' in body)) return null;
      if (body.status === 'completed' && 'referenceNo' in body && 'feeKurus' in body) {
      const { referenceNo, feeKurus } = body;
      if (typeof referenceNo === 'string' && typeof feeKurus === 'number' && Number.isInteger(feeKurus)) {
      return { status: 'completed', referenceNo, feeKurus };
      }
      }
      if (body.status === 'queued' && 'sendAt' in body && typeof body.sendAt === 'string') {
      return { status: 'queued', sendAt: body.sendAt };
      }
      if (body.status === 'rejected' && 'reason' in body && isRejectReason(body.reason)) {
      return { status: 'rejected', reason: body.reason };
      }
      return null;
      }

      src/features/transfer/ transferMessage.ts Her durum için bir mesaj; dördüncü bir durum eklenince derleme durur.

      src/features/transfer/transferMessage.ts
      import type { RejectReason, TransferResult } from './transfer';
      // A Record over the union is exhaustive too: a new reason without a text will not compile.
      const REJECT_TEXT: Record<RejectReason, string> = {
      INSUFFICIENT_FUNDS: 'Bakiye yetersiz.',
      DAILY_LIMIT_EXCEEDED: 'Günlük transfer limitin doldu.',
      FRAUD_HOLD: 'Güvenlik kontrolü için bekletiliyor, seni arayacağız.',
      };
      export function transferMessage(result: TransferResult): string {
      switch (result.status) {
      case 'completed':
      // === 0, not !feeKurus: a free FAST transfer really has a fee of 0.
      return result.feeKurus === 0
      ? `Gönderildi, ücret alınmadı. Ref: ${result.referenceNo}`
      : `Gönderildi. Ref: ${result.referenceNo}`;
      case 'queued':
      return `EFT sıraya alındı, ${result.sendAt} saatinde gönderilecek.`;
      case 'rejected':
      return REJECT_TEXT[result.reason];
      default: {
      // Add a fourth status and this line stops compiling.
      const exhaustive: never = result;
      return exhaustive;
      }
      }
      }

      src/features/transfer/ transferMessage.alternative.ts Şöyle de yazılabilirdi: as ile kısa yoldan. Bak, ücretsiz bir FAST işlemi ücret bilgisi yok gibi görünüyor.

      src/features/transfer/transferMessage.alternative.ts
      // Another way to write it: shorter, and the compiler stays quiet.
      export function transferMessage(body: unknown): string {
      const result = body as { status: string; referenceNo?: string; feeKurus?: number };
      if (result.status === 'completed') {
      // A free FAST transfer sends feeKurus: 0, and !0 is true.
      if (!result.feeKurus) return 'Ücret bilgisi alınamadı.';
      return `Gönderildi. Ref: ${result.referenceNo}`;
      }
      // 'queued' lands here too, and so will any status added next year.
      return 'İşlem reddedildi.';
      }
      Kafam karıştı, daha basit anlat

      never “buraya hiçbir zaman gelinmemeli” demektir. Yeni bir şekil ekleyip onu unuttuğunda, derleyici tam o satırı gösterir.

      Hızlı kontrolBaşlangıç

      `strictNullChecks` kapalıyken ne kaybedersin?

      Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

      Tuzaklar: any yerine unknown

      Aynı satır, farklı sonuç
      const fromApi: any = JSON.parse(raw);
      fromApi.user.name; // derlenir → user yoksa çalışma zamanında çöker
      const safe: unknown = JSON.parse(raw);
      safe.user; // DERLENMEZ — önce daraltmalısın

      any tip kontrolünü o değer için kapatır. unknown ise “değer var ama tipini bilmiyorum” der ve kullanmadan önce kanıt ister.

      Kural: dışarıdan gelen her veri unknown’dır. JSON.parse, API cevabı, localStorage, form girdisi. Sınırda bir kez doğrula (Zod gibi bir şema doğrulayıcı en pratik yol), içeride tiplerle çalış.

      Tip güvenliği ≠ mantık doğruluğu. Bu bölüm dersin en önemli kısmı olabilir.

      Derlenir, tip kontrolünden geçer, yanlıştır
      function label(count: number | undefined): string {
      if (!count) return "belirtilmedi"; // count === 0 de buraya düşer
      return `${count} adet`;
      }
      label(0); // "belirtilmedi" ← hata

      0 falsy’dir. Derleyici itiraz etmez çünkü kod tip açısından tamamen geçerlidir; yanlış olan iş mantığıdır.

      Doğrusu
      function label(count: number | undefined): string {
      if (count === undefined) return "belirtilmedi";
      return `${count} adet`;
      }

      Aynı tuzak metinlerde boş string ("") ile kurulur. ?? (nullish coalescing) ile || arasındaki fark da tam burada:

      const a = 0 || 10; // 10 — 0 falsy sayıldı
      const b = 0 ?? 10; // 0 — yalnızca null/undefined elenir

      Günlük kullanımda birkaç utility type.

      TipNe yapar
      Partial<T>Tüm alanlar opsiyonel — güncelleme DTO’ları
      Required<T>Tüm alanlar zorunlu
      Pick<T, K> / Omit<T, K>Alan seçme / çıkarma
      Readonly<T>Değiştirilemez hâle getirir
      Record<K, V>Anahtar-değer eşlemesi
      ReturnType<F>Bir fonksiyonun dönüş tipi

      Kendini sına

      Şimşek turu1/5

      as, değerin tipini çalışma anında kontrol eder.

      Soru 1/3Başlangıç

      Bu fonksiyon `label(0)` için ne döndürür?

      Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.
      label.ts
      1function label(count: number | undefined): string {
      2 if (!count) {
      3 return "belirtilmedi";
      4 }
      5
      6 return `${count} adet`;
      7}
      8
      9console.log(label(0));
      TypeScriptUTF-8LF

      Aklında kalacak üç şey

      1. 1 as derleyiciye susmasını söyler ve hiçbir şeyi kontrol etmez; daraltma if ve typeof gibi kontrollerle kanıt üretir. Kodda çok sayıda as görmek daraltmanın yapılmadığının işaretidir.
      2. 2 any tip kontrolünü kapatır ve hatayı çalışma anına erteler. Dışarıdan gelen her veri unknown olmalıdır.
      3. 3 Tip doğru diye kod doğru olmaz: sayılarda 0, metinlerde boş string false sayılır. undefined kontrol edeceksen === undefined yaz.
      Sonraki kapı console.log, setTimeout(0), Promise.then, console.log. Konsol hangi sırayla yazar? JavaScript Olay Döngüsü — setTimeout(0) Neden Sonra Yazıyor? · 9 dk

      4 kart sonraki derste seni bekliyor

      0/5 kart bu dersten toplandı