TypeScript Type Guards: Tip Korumaları

TypeScript Type Guards: Tip Korumaları
Type guard'lar, çalışma zamanında bir değerin tipini kontrol ederek TypeScript'in tip daraltma yapmasını sağlayan ifadelerdir. Doğru kullanıldığında, union type'lar ile çalışırken hem tip güvenliğini hem de kodun okunabilirliğini artırır. Bu yazıda tüm type guard tekniklerini kapsamlı örneklerle ele alacağız.
Yerleşik Type Guard'lar
// typeof type guard
function islemYap(deger: string | number | boolean): string {
if (typeof deger === "string") {
return `Metin: ${deger.toUpperCase()}`;
}
if (typeof deger === "number") {
return `Sayı: ${deger.toFixed(2)}`;
}
return `Boolean: ${deger ? "Evet" : "Hayır"}`;
}
// instanceof type guard
class Dikdortgen {
constructor(public genislik: number, public yukseklik: number) {}
}
class Daire {
constructor(public yaricap: number) {}
}
function alanHesapla(sekil: Dikdortgen | Daire): number {
if (sekil instanceof Dikdortgen) {
return sekil.genislik * sekil.yukseklik;
}
return Math.PI * sekil.yaricap ** 2;
}
// in type guard
interface Kus { kanatAcikligi: number; uc(): void; }
interface Balik { yuzgecSayisi: number; yuz(): void; }
function hareketEt(hayvan: Kus | Balik): void {
if ("uc" in hayvan) {
console.log(`Kanat açıklığı: ${hayvan.kanatAcikligi}`);
hayvan.uc();
} else {
console.log(`Yüzgeç sayısı: ${hayvan.yuzgecSayisi}`);
hayvan.yuz();
}
}
User-Defined Type Guards (is keyword)
Kendi type guard fonksiyonlarınızı is anahtar kelimesi ile oluşturabilirsiniz:
// Basit type guard
interface Kullanici {
tip: "kullanici";
isim: string;
email: string;
}
interface Admin {
tip: "admin";
isim: string;
email: string;
yetkiler: string[];
}
type Kisi = Kullanici | Admin;
function adminMi(kisi: Kisi): kisi is Admin {
return kisi.tip === "admin";
}
function yetkiKontrol(kisi: Kisi): void {
if (adminMi(kisi)) {
console.log(`Admin yetkileri: ${kisi.yetkiler.join(", ")}`);
} else {
console.log(`Standart kullanıcı: ${kisi.isim}`);
}
}
// Generic type guard
function diziMi<T>(deger: T | T[]): deger is T[] {
return Array.isArray(deger);
}
function islemYap<T>(girdi: T | T[]): T[] {
if (diziMi(girdi)) {
return girdi;
}
return [girdi];
}
// Null/undefined type guard
function tanimliMi<T>(deger: T | null | undefined): deger is T {
return deger !== null && deger !== undefined;
}
const degerler = ["Ali", null, "Ayşe", undefined, "Mehmet"];
const temizDegerler = degerler.filter(tanimliMi);
// string[] - null ve undefined otomatik filtrelendi!
filter ile birlikte type guard kullanmak, dizi filtreleme sonrasında tipin doğru daraltılmasını sağlar. filter(Boolean) bu etkiyi sağlamaz; custom type guard fonksiyonu kullanın.
Discriminated Union ile Type Guard
// API yanıt tipleri
interface YuklenmeDurumu {
durum: "yukleniyor";
}
interface BasariliDurum<T> {
durum: "basarili";
veri: T;
}
interface HataDurumu {
durum: "hata";
mesaj: string;
kod: number;
}
type AsyncDurum<T> = YuklenmeDurumu | BasariliDurum<T> | HataDurumu;
// Type guard fonksiyonları
function yuklenmeDurumundaMi<T>(d: AsyncDurum<T>): d is YuklenmeDurumu {
return d.durum === "yukleniyor";
}
function basariliMi<T>(d: AsyncDurum<T>): d is BasariliDurum<T> {
return d.durum === "basarili";
}
function hataMi<T>(d: AsyncDurum<T>): d is HataDurumu {
return d.durum === "hata";
}
// Kullanım
function durumGoster<T>(durum: AsyncDurum<T>): string {
if (yuklenmeDurumundaMi(durum)) {
return "Yükleniyor...";
}
if (basariliMi(durum)) {
return `Veri: ${JSON.stringify(durum.veri)}`;
}
return `Hata (${durum.kod}): ${durum.mesaj}`;
}
Assertion Functions
// asserts keyword ile runtime doğrulama
function assertString(deger: unknown): asserts deger is string {
if (typeof deger !== "string") {
throw new TypeError(`String beklendi, ${typeof deger} alındı`);
}
}
function assertNonNull<T>(
deger: T,
mesaj?: string
): asserts deger is NonNullable<T> {
if (deger === null || deger === undefined) {
throw new Error(mesaj ?? "Değer null/undefined olamaz");
}
}
// Kullanım
function veriyiIsle(girdi: unknown): void {
assertString(girdi);
// Bu satırdan sonra girdi kesinlikle string
console.log(girdi.toUpperCase());
}
function kullaniciIsle(kullanici: Kullanici | null): void {
assertNonNull(kullanici, "Kullanıcı bulunamadı");
console.log(kullanici.isim); // Güvenli
}
// Koşullu assertion
function assertPozitif(sayi: number): asserts sayi is number {
if (sayi <= 0) {
throw new RangeError(`Pozitif sayı beklendi, ${sayi} alındı`);
}
}
asserts imzası ile tutarlı olduğundan emin olun.
İleri Seviye Type Guard Pattern'leri
// Nesne şekli doğrulama
interface ApiKullanici {
id: number;
name: string;
email: string;
}
function apiKullaniciMi(deger: unknown): deger is ApiKullanici {
if (typeof deger !== "object" || deger === null) return false;
const obj = deger as Record<string, unknown>;
return (
typeof obj.id === "number" &&
typeof obj.name === "string" &&
typeof obj.email === "string"
);
}
// Zod benzeri runtime doğrulama
async function apiIstegi<T>(
url: string,
guard: (deger: unknown) => deger is T
): Promise<T> {
const response = await fetch(url);
const data = await response.json();
if (!guard(data)) {
throw new Error("API yanıtı beklenen formatta değil");
}
return data;
}
// Kullanım
const kullanici = await apiIstegi("/api/kullanici/1", apiKullaniciMi);
console.log(kullanici.name); // Tip güvenli!
Sonuç
Type guard'lar, TypeScript'in tip daraltma mekanizmasının temel yapı taşlarıdır. typeof, instanceof, in operatörü, custom type guard'lar ve assertion function'lar ile union type'ları güvenle daraltabilirsiniz. Discriminated union pattern'i ile birlikte kullanıldığında, çalışma zamanı hatalarını minimize eden güçlü ve okunabilir kod yazabilirsiniz.