8 Ekim 2026

TypeScript Tip Güvenliği Yanılsaması ve Defansif Mimari

typescriptreactzod

TypeScript kullanan neredeyse her yazılım geliştirici, derleyicinin (tsc) onayladığı ve sıfır hata ile derlenen bir kodun canlı ortamda 'Cannot read properties of undefined' hatasıyla çöktüğünü en az bir kez tecrübe etmiştir. Statik analiz araçları geliştikçe kod kalitemiz artsa da, TypeScript'in sunduğu tip güvenliği sözü, derleme aşamasından sonra JavaScript'in dinamik doğasına adım attığımız anda son bulur. Bu durum, özellikle dış dünyayla etkileşime girdiğimiz sınırlarda ciddi üretim ortamı hatalarına yol açar.

Derleme Zamanı Güvenliği Neden Yetersiz Kalır?

TypeScript, özünde yalnızca derleme zamanında (compile-time) çalışan bir statik analiz aracıdır. JavaScript'e derlendiği anda tüm arayüzler (interfaces), tipler (types) ve jenerikler ortadan kalkar; geriye sadece çıplak JavaScript kodu kalır. Bu işleme tip silinmesi (type erasure) denir. Dolayısıyla, uygulamanızın dış dünyayla temas ettiği noktalarda (API yanıtları, yerel depolama, URL parametreleri veya kullanıcı girdileri) TypeScript derleyicisinin hiçbir gücü ve söz hakkı yoktur.

Birçok projede, ağ üzerinden gelen verileri doğrudan TypeScript tiplerine zorlamak (type casting) için as User veya benzeri ifadeler kullanılır. Bu yaklaşım, aslında derleyiciye bir yalan söylemektir. API'den gelen verinin formatı değiştiğinde veya eksik geldiğinde, derleyici bu verinin doğruluğundan emin olduğunu varsayar ve kodun çalışmasına izin verir. Ancak çalışma zamanında (runtime) eksik bir alan üzerinden işlem yapılmaya çalışıldığında uygulama kaçınılmaz olarak çöker. Tek sayfa uygulamalarında (SPA) tek bir bileşenin bu şekilde çökmesi, tüm uygulamanın beyaz ekrana dönüşmesine yol açabilir.

Parse, Don't Validate Yaklaşımı ve Tip Daraltma

Geleneksel doğrulama yöntemleri, verinin geçerli olup olmadığını kontrol edip boolean bir değer döndürmekle yetinir. Bu durum, doğrulanan veriyi hala güvensiz kabul etmemize neden olur çünkü veri tipi hala geniş bir kümedir. "Parse, don't validate" (Doğrulama, ayrıştır) yaklaşımı ise veriyi kontrol etmekle kalmaz; başarılı olması durumunda veriyi daha dar ve kesin garantileri olan yeni bir tipe dönüştürür. Bu prensip, güven sınırını uygulamanın en dış çeperine çekmeyi amaçlar.

"Parse, don't validate" prensibi, veriyi doğrulamakla kalmaz; verinin yapısını doğrulandığı andan itibaren güvenilir bir tipe dönüştürür.

Ayrıştırma işlemi başarılı olduğunda, verinin tam olarak beklediğimiz yapıda olduğunu çalışma zamanında kanıtlamış oluruz. Eğer veri şablona uymuyorsa, sistem daha uygulamanın iç katmanlarına sızmadan hatayı sınırda yakalar ve yönetilebilir bir duruma getirir. Böylece, uygulamanın çekirdek iş mantığı (business logic) her zaman temiz, tutarlı ve kesinlikle doğru tipleştirilmiş verilerle çalışır. Bu yaklaşım, karmaşık if-else bloklarını ve savunma amaçlı yazılan null-check kodlarını büyük ölçüde azaltır.

Zod ve Valibot Karşılaştırması: Mimari Seçimler

Çalışma zamanı şeması doğrulaması söz konusu olduğunda günümüzde iki popüler kütüphane öne çıkmaktadır: Zod ve Valibot. Zod, zincirleme (method chaining) API tasarımıyla geliştirici deneyimini en üst düzeye çıkaran, endüstri standardı haline gelmiş bir araçtır. Ancak Zod, monolitik yapısı nedeniyle ağaç silkeleme (tree-shaking) işlemine tam uyum sağlayamaz ve uygulamanın bundle boyutuna yaklaşık 15KB (gzipped) yük getirebilir. Bu durum, özellikle performans hassasiyeti yüksek web uygulamalarında dikkate alınması gereken bir maliyettir.

Diğer taraftan Valibot, modüler ve fonksiyonel tasarımıyla öne çıkar. Yalnızca kullandığınız doğrulama fonksiyonlarını import etmenize olanak tanır. Bu sayede kullanılmayan kodlar modern derleyiciler tarafından elenir ve bundle boyutu 1-2KB seviyelerine kadar düşebilir. Her iki kütüphane de TypeScript ile mükemmel entegrasyona sahiptir ve şemalardan otomatik olarak statik tipler türetebilir. Aşağıdaki basit örnek, Zod kullanarak güvenli bir ayrıştırma işleminin nasıl yapıldığını göstermektedir:

import { z } from 'zod';
const UserSchema = z.object({
  id: z.string().uuid(),
  email: z.string().email(),
  role: z.enum(['admin', 'user'])
});
const result = UserSchema.safeParse(JSON.parse(apiResponse));
if (!result.success) {
  console.error(result.error.format());
}

React Uygulamalarında Hata Sınırları ve Çökme Önleme

Şema tabanlı ayrıştırmayı React mimarisine entegre ederken, veri çekme katmanını (data fetching layer) ilk savunma hattı olarak konumlandırmak gerekir. TanStack Query (React Query) veya Axios interceptor yapıları içerisinde, gelen API yanıtlarını doğrudan şemadan geçirmek en sağlıklı yöntemdir. Şema doğrulaması başarısız olduğunda fırlatılan hatalar, uygulamanın genelini çökertmek yerine ilgili bileşen seviyesinde izole edilmelidir.

Bunun için React'in Error Boundary (Hata Sınırı) mekanizmasıyla şema doğrulaması birlikte çalıştırılmalıdır. Bir widget veya sayfa bölümü, API'deki beklenmedik bir değişiklik yüzünden veri ayrıştırma hatası aldığında, tüm ekranın beyaz sayfaya düşmesi engellenir. Bunun yerine sadece ilgili bileşen hata durumuna geçerken, uygulamanın geri kalan kısımları sorunsuz çalışmaya devam eder. Bu stratejiyi kurarken şu adımlara dikkat edilmelidir:

  • Veri sınırlarında asla doğrudan tip zorlaması (type assertion) yapmayın.
  • Büyük bundle boyutlarından kaçınmak için mikro uygulamalarda Valibot, geniş ekosistem desteği için Zod tercih edin.
  • Şema doğrulama hatalarını sessizce yutmak yerine Sentry gibi izleme araçlarına detaylı hata logları ile birlikte gönderin.

Sonuç olarak, TypeScript'in sunduğu güvenliği gerçek anlamda çalışma zamanına taşımak, uygulamanın kararlılığını doğrudan artıran mimari bir karardır. Projelerinizde veri sınırlarını belirleyerek bu sınırları çalışma zamanı şemalarıyla korumak, öngörülemeyen çökmelerin önüne geçmek için en etkili yoldur. Bir sonraki adım olarak, projenizdeki en kritik API entegrasyon noktasını belirleyip oraya basit bir Zod veya Valibot şeması ekleyerek mimarinizi güçlendirmeye başlayabilirsiniz.