Tip Mimarisi Neden Ayrı Bir Konu?
TypeScript'in tip sistemi, yanlış kullanıldığında güvenlik hissi verir ama sağlamaz. any ile susturulmuş bir derleyici hatası, çalışma zamanında çöken bir servise dönüşür. Tip mimarisi, tipleri yalnızca "derleyiciye takılmayalım" aracı olarak değil, alan modelini ve değişmezleri (invariants) kodda ifade etme yöntemi olarak ele alır.
Bu rehber orta seviye geliştiriciler için yazıldı. Temel TypeScript söz dizimini (interface, generics, union tipler) bildiğinizi varsayıyorum. Sürüm notu: Aşağıdaki örnekler TypeScript 5.x davranışına göredir; satisfies operatörü (5.0) ve const tip parametreleri (5.0) bu sürümde mevcuttur. Daha eski sürümlerde bu iki özellik çalışmaz.
1. any'yi Sınırda Tut, İçeride Yasakla
Güvenli tip mimarisinin ilk kuralı: belirsizliğin girdiği noktayı bilmek. Ağdan, dosyadan, localStorage'dan gelen veri çalışma zamanında doğrulanmadan güvenilir değildir. any bu sınırda kalmalı, iç katmanlara sızmamalıdır.
// Sınır katmanı: dış veri "unknown" olarak gelir
async function fetchUser(id: string): Promise<unknown> {
const res = await fetch(`/api/users/${id}`);
return res.json();
}
unknown, any'nin aksine kullanılmadan önce daraltma (narrowing) gerektirir. Bu, doğrulamayı zorunlu kılar. Doğrulamayı elle yazmak yerine bir şema kütüphanesi kullanmak yaygın bir yaklaşımdır; ancak kütüphane seçimi ve API'si sürüme göre değiştiği için burada somut bir kütüphane örneği vermiyorum. Kural şu: sınırda doğrula, içeride tipli veriyle çalış.
2. Tipi Değerden Türet, Ayrı Ayrı Yazma
Aynı bilgiyi iki yerde tutmak (hem çalışma zamanı değeri hem tip) zamanla senkron bozulmasına yol açar. Bunun yerine tipi mevcut değerden türetin.
const ROLES = ["admin", "editor", "viewer"] as const;
type Role = (typeof ROLES)[number];
// Role = "admin" | "editor" | "viewer"
as const olmadan ROLES tipi string[] olur ve Role de string'e düşer — yani daralma kaybolur. Bu, yaygın bir hata noktasıdır.
3. Ayrık Birleşimlerle (Discriminated Unions) Geçersiz Durumları İmkânsız Kıl
Bir nesnenin "yükleniyor" iken aynı zamanda "hata" olması mantıksal olarak saçmadır. Bu tür durumları tek bir düz nesne yerine ayrık birleşimle modelleyin.
type RequestState<T> =
| { status: "idle" }
| { status: "loading" }
| { status: "success"; data: T }
| { status: "error"; message: string };
function render<T>(state: RequestState<T>): string {
switch (state.status) {
case "idle":
return "";
case "loading":
return "Yükleniyor…";
case "success":
return JSON.stringify(state.data); // state.data burada güvenli
case "error":
return state.message;
}
}
switch içinde her dalda status daraltılır ve ilgili alanlar (data, message) erişilebilir olur. success dalında state.data'ya erişmek derleyici tarafından garanti edilir; başka dalda erişmeye çalışmak derleme hatası verir.
Beklenen davranış: Tüm dallar ele alındığında fonksiyonun dönüş tipi tatmin edilir. Yeni bir status eklenip bir case unutulursa, noImplicitReturns veya strict ayarlarıyla derleyici sizi uyarır.
4. satisfies ile Doğrula ama Daraltmayı Kaybetme
Bir nesnenin belirli bir şekle uyduğunu garanti etmek isterken tipini genişletmek istemeyebilirsiniz. satisfies (TypeScript 5.0+) tam bu iş için vardır.
type Config = Record<string, string | number>;
const config = {
host: "localhost",
port: 8080,
} satisfies Config;
// config.port hâlâ "number", config.host hâlâ "string"
// ama Config şekline uymadığında derleme hatası alırsınız
const config: Config = {...} yazsaydınız, config.port tipi string | number'a genişlerdi ve sayısal işlem yapamazdınız. satisfies bu kaybı önler.
5. Yaygın Hatalar
asile zorla daraltma:value as Useryazmak, doğrulamayı atlar. Çalışma zamanındavaluebeklediğiniz şekilde değilse hata sessizce ilerler.as'i yalnızca gerçekten emin olduğunuz ve gerekçesini yorumda belirttiğiniz yerlerde kullanın.as constunutmak: YukarıdakiROLESörneğinde olduğu gibi, literal tiplerin daralmasını kaybettirir.enumyerine birleşim kullanmamak: TypeScriptenum'ları derleme sonrası ek kod üretir ve sayısal enum'lar beklenmedik değerlere izin verir. Çoğu durumdaas constdizi + türetilmiş tip daha güvenlidir.- Genel
objectveya{}kullanmak:{}neredeyse her şeyi kabul eder;objectise ilkel türleri dışlar ama alan bilgisi vermez. Alanları açıkça tanımlayın.
6. Güvenlik Etkileri
Tip doğrulaması bir güvenlik sınırı değildir; çalışma zamanı doğrulamasının yerini tutmaz. Kritik noktalar:
- Girdi doğrulaması: API ve form girdileri çalışma zamanında doğrulanmalıdır. Tipler yalnızca geliştirme zamanında yardımcı olur.
- Yetkilendirme:
Roletipi "admin" değerini kabul ediyor diye, o değerin sunucuda doğrulanmış olduğu anlamına gelmez. Yetki kontrolü sunucu tarafında yapılmalıdır. - Serileştirme sınırları:
JSON.parseçıktısı her zamanunknown'dır; tip parametresiyleJSON.parse(x) as Tyazmak doğrulama yapmaz.
Özet
Güvenli tip mimarisi üç ilkeye dayanır: belirsizliği sınırda unknown ile karşıla, tipi tek kaynaktan türet, geçersiz durumları ayrık birleşimlerle ifade edilemez kıl. satisfies ve as const gibi araçlar daralmayı korurken doğrulama sağlar. Tiplerin geliştirme zamanı garantisi olduğunu, çalışma zamanı doğrulamasının ve sunucu tarafı yetkilendirmenin yerini tutmadığını unutmayın.