TypeScript’in sağladığı tip güvenliğinden yararlanmak için her nesneye bir interface tanımlamak tek başına yeterli değildir. Gerçek projelerde bir değer birden fazla biçimde gelebilir, aynı fonksiyon farklı veri tipleriyle çalışabilir veya mevcut bir tipin yalnızca belirli alanlarına ihtiyaç duyulabilir.
Bu gibi durumlarda tipleri kopyalamak, bütün alanları opsiyonel yapmak veya any kullanmak kodun güvenilirliğini azaltır. TypeScript’in union, generic ve utility type özellikleri; gerçek iş kurallarını daha doğru modellemeyi, tekrarları azaltmayı ve hataları geliştirme aşamasında yakalamayı sağlar.
Bu rehberde union, generic ve utility type yapılarını ayrı ayrı inceleyecek, ardından gerçek projelerde nasıl birlikte kullanılabileceklerini örneklerle göstereceğiz.
Union Type Nedir?
Union type, bir değişkenin birden fazla olası tipten birini alabileceğini belirtir. Tipler dikey çizgi, yani | karakteriyle birleştirilir.
let kullaniciId: string | number;
kullaniciId = 42;
kullaniciId = "usr_42";Bu değişkene yalnızca string veya number atanabilir:
kullaniciId = true;
// Hata: boolean atanamaz.Union type, bir değerin olası biçimlerini any kullanmadan ifade etmemizi sağlar.
Union Type Neden any Kullanımından Daha Güvenlidir?
Aşağıdaki fonksiyon any kullandığı için bütün değerleri kabul eder:
function kimlikYazdir(id: any) {
console.log(id.toUpperCase());
}
kimlikYazdir(42);Kod derlenir ancak çalışma zamanında hata oluşur. Union type ile yalnızca desteklenen tipleri kabul edebilir ve her durumu güvenli biçimde işleyebiliriz:
function kimlikYazdir(id: string | number): string {
if (typeof id === "string") {
return id.toUpperCase();
}
return id.toString();
}TypeScript, typeof kontrolünden sonra ilgili bloktaki tipi otomatik olarak daraltır.
Literal Union ile Seçenekleri Sınırlandırmak
Union yalnızca string ve number gibi temel tiplerle kullanılmaz. Belirli değerlerden oluşan seçenek listeleri de oluşturulabilir.
type KullaniciRolu = "admin" | "editor" | "viewer";
function yetkiKontrol(rol: KullaniciRolu) {
if (rol === "admin") {
return "Tam yetki";
}
return "Sınırlı yetki";
}Fonksiyon yalnızca tanımlanan üç rolü kabul eder:
yetkiKontrol("admin");
yetkiKontrol("editor");
yetkiKontrol("super-admin");
// Hata: Geçerli bir KullaniciRolu değil.Literal union; durum, rol, tema, ödeme yöntemi ve sipariş aşaması gibi sınırlı seçenekleri modellemek için kullanılabilir.
type SiparisDurumu =
| "bekliyor"
| "hazirlaniyor"
| "kargoda"
| "teslim-edildi"
| "iptal";Bu yaklaşım, yazım hatalarını ve sistemin desteklemediği değerlerin kullanılmasını önler.
Discriminated Union ile Durum Modellemek
Gerçek uygulamalarda bir nesnenin alanları mevcut durumuna göre değişebilir. Örneğin bir API isteği yükleniyor, başarılı veya hatalı olabilir.
Zayıf bir model şöyle görünebilir:
interface ApiDurumu<T> {
status: string;
data?: T;
error?: string;
}Bu model aşağıdaki mantıksız nesneye izin verebilir:
const durum: ApiDurumu<string> = {
status: "success",
error: "Bir hata oluştu",
};Başarılı bir durumda veri bulunmazken hata mesajı bulunması geçerli kabul edilmiştir.
Discriminated union kullanarak her durumun gerektirdiği alanları açıkça tanımlayabiliriz:
type ApiDurumu<T> =
| { durum: "bekliyor" }
| { durum: "yukleniyor" }
| { durum: "basarili"; veri: T }
| { durum: "hatali"; hata: string };Kullanım sırasında durum alanı kontrol edildiğinde TypeScript nesnenin geri kalan yapısını bilir:
function sonucuGoster(durum: ApiDurumu<string>): string {
switch (durum.durum) {
case "bekliyor":
return "İstek henüz başlamadı.";
case "yukleniyor":
return "Yükleniyor...";
case "basarili":
return durum.veri;
case "hatali":
return durum.hata;
}
}Bu yöntem, mümkün olmayan durumların daha en başta oluşturulmasını engeller.
Union Type Kullanırken Exhaustive Check
Union yapısına yeni bir seçenek eklendiğinde bütün kontrollerin güncellenmesi gerekir. never tipiyle eksik durumları derleme aşamasında yakalayabiliriz.
type OdemeDurumu =
| { tip: "bekliyor" }
| { tip: "tamamlandi"; islemId: string }
| { tip: "reddedildi"; neden: string };
function assertNever(deger: never): never {
throw new Error(`Beklenmeyen değer: ${JSON.stringify(deger)}`);
}
function odemeMesaji(durum: OdemeDurumu): string {
switch (durum.tip) {
case "bekliyor":
return "Ödeme bekliyor.";
case "tamamlandi":
return `İşlem: ${durum.islemId}`;
case "reddedildi":
return durum.neden;
default:
return assertNever(durum);
}
}OdemeDurumu içine yeni bir seçenek eklenir ancak switch güncellenmezse TypeScript assertNever satırında hata üretir.
Generic Nedir?
Generic, bir fonksiyonun, tipin veya sınıfın farklı veri tipleriyle çalışmasını sağlarken tip bilgisini korur.
Aşağıdaki fonksiyon any kullandığı için giriş ve çıkış arasındaki ilişkiyi kaybeder:
function ilkEleman(liste: any[]): any {
return liste[0];
}
const sonuc = ilkEleman(["TypeScript", "Node.js"]);sonuc değerinin tipi any olur. Generic kullanarak dizinin eleman tipini dönüş değerine taşıyabiliriz:
function ilkEleman<T>(liste: T[]): T | undefined {
return liste[0];
}
const teknoloji = ilkEleman(["TypeScript", "Node.js"]);
// string | undefined
const puan = ilkEleman([90, 85, 100]);
// number | undefinedBuradaki T, fonksiyon çağrılırken belirlenen tipi temsil eder. TypeScript çoğu zaman bu tipi parametreden otomatik olarak çıkarır.
Generic Neden any Değildir?
any, tip bilgisini ortadan kaldırır. Generic ise tipler arasındaki ilişkiyi korur.
function aynisiniDondur<T>(deger: T): T {
return deger;
}
const metin = aynisiniDondur("Merhaba");
// string
const sayi = aynisiniDondur(42);
// numberFonksiyon farklı tiplerle çalışabilir ancak girişte aldığı tipi dönüş değerinde korur.
Karşılaştırma:
function anyIleDondur(deger: any): any {
return deger;
}
const sonuc = anyIleDondur("Merhaba");
// anyGeneric esneklik sağlar; any ise kontrolü kaldırır.
Birden Fazla Generic Parametre Kullanmak
Bir fonksiyon birden fazla tip arasındaki ilişkiyi koruyabilir:
function eslestir<A, B>(birinci: A, ikinci: B): [A, B] {
return [birinci, ikinci];
}
const sonuc = eslestir("ürün", 42);
// [string, number]Nesne birleştirme örneği:
function birlestir<A extends object, B extends object>(
birinci: A,
ikinci: B,
): A & B {
return {
...birinci,
...ikinci,
};
}
const kullanici = birlestir(
{ id: 1, ad: "Rıfat" },
{ aktif: true },
);
console.log(kullanici.ad);
console.log(kullanici.aktif);Dönüş tipi her iki nesnenin alanlarını içerir.
Generic Constraint Nedir?
Generic bir yapı her tipi kabul ettiğinde, tip üzerinde hangi işlemlerin yapılabileceği sınırlıdır.
function uzunluguYaz<T>(deger: T) {
return deger.length;
// Hata: T tipinde length bulunduğu garanti değil.
}T tipini belirli bir yapıyla sınırlandırabiliriz:
interface UzunlukSahibi {
length: number;
}
function uzunluguYaz<T extends UzunlukSahibi>(
deger: T,
): number {
return deger.length;
}
uzunluguYaz("TypeScript");
uzunluguYaz([1, 2, 3]);
uzunluguYaz({ length: 10 });
uzunluguYaz(42);
// Hata: number tipinde length yok.Constraint, generic yapının esnekliğini korurken kullanılabilen tipleri sınırlar.
keyof ile Güvenli Özellik Erişimi
Bir nesnenin alan adına göre değer okumak istediğimizde string kullanmak güvenli değildir:
function alanOku(nesne: object, anahtar: string) {
return nesne[anahtar];
}keyof ve generic kullanarak anahtarın gerçekten nesneye ait olmasını sağlayabiliriz:
function alanOku<T, K extends keyof T>(
nesne: T,
anahtar: K,
): T[K] {
return nesne[anahtar];
}
const kullanici = {
id: 1,
ad: "Rıfat",
aktif: true,
};
const ad = alanOku(kullanici, "ad");
// string
const aktif = alanOku(kullanici, "aktif");
// boolean
alanOku(kullanici, "email");
// Hata: email nesnede bulunmuyor.T[K], seçilen anahtara karşılık gelen alanın tipini döndürür.
Generic Interface Kullanımı
API yanıtları, sayfalama yapıları ve ortak bileşenler generic interface ile modellenebilir.
interface ApiYaniti<T> {
basarili: boolean;
veri: T;
mesaj?: string;
}
interface Kullanici {
id: number;
ad: string;
}
const yanit: ApiYaniti<Kullanici> = {
basarili: true,
veri: {
id: 1,
ad: "Rıfat",
},
};Liste yanıtı için:
interface SayfalanmisYanit<T> {
kayitlar: T[];
toplam: number;
sayfa: number;
sayfaBoyutu: number;
}
const kullanicilar: SayfalanmisYanit<Kullanici> = {
kayitlar: [
{
id: 1,
ad: "Rıfat",
},
],
toplam: 1,
sayfa: 1,
sayfaBoyutu: 20,
};Aynı sayfalama yapısı ürün, sipariş veya makale gibi farklı veri tipleriyle tekrar kullanılabilir.
Generic Varsayılan Tipler
Generic parametrelere varsayılan tip verilebilir:
interface ApiCevabi<T = unknown> {
veri: T;
durumKodu: number;
}
const genelCevap: ApiCevabi = {
veri: {
mesaj: "Tamam",
},
durumKodu: 200,
};
const metinCevabi: ApiCevabi<string> = {
veri: "Tamam",
durumKodu: 200,
};Tip belirtilmediğinde unknown kullanılması, any kullanımına göre daha güvenli bir varsayılandır.
Utility Type Nedir?
Utility type’lar, mevcut tiplerden yeni tipler üretmek için TypeScript tarafından sunulan hazır yardımcı tiplerdir.
Örneğin bir Kullanici tipi üzerinden:
- Bütün alanları opsiyonel yapabilir,
- Yalnızca belirli alanları seçebilir,
- Bazı alanları çıkarabilir,
- Alanları salt okunur hâle getirebilir,
- Fonksiyon dönüş tipini elde edebiliriz.
Bu sayede aynı veri yapısını küçük değişikliklerle tekrar tekrar yazmamız gerekmez.
Partial<T>: Bütün Alanları Opsiyonel Yapmak
Partial<T>, verilen tipteki bütün alanları opsiyonel hâle getirir.
interface Kullanici {
id: number;
ad: string;
email: string;
aktif: boolean;
}
function kullaniciGuncelle(
id: number,
degisiklikler: Partial<Kullanici>,
) {
// Güncelleme işlemi
}
kullaniciGuncelle(1, {
ad: "Rıfat Akarca",
});
kullaniciGuncelle(1, {
email: "yeni@example.com",
aktif: false,
});Güncelleme fonksiyonunda bütün alanların gönderilmesi gerekmediği için Partial uygundur.
Ancak Partial<Kullanici>, id alanını da opsiyonel yapar. Güncellenmemesi gereken alanları önce çıkarmak daha doğru olabilir:
type KullaniciGuncelleme = Partial<
Omit<Kullanici, "id">
>;Required<T>: Bütün Alanları Zorunlu Yapmak
Required<T>, opsiyonel alanları zorunlu hâle getirir.
interface UygulamaAyarlari {
tema?: "acik" | "koyu";
dil?: "tr" | "en";
bildirimler?: boolean;
}
type TamAyarlar = Required<UygulamaAyarlari>;
const ayarlar: TamAyarlar = {
tema: "koyu",
dil: "tr",
bildirimler: true,
};Yapılandırma değerleri ilk aşamada opsiyonel alınabilir, varsayılanlarla birleştirildikten sonra tam ve zorunlu bir tipe dönüştürülebilir.
Pick<T, K>: Belirli Alanları Seçmek
Pick, mevcut bir tipten yalnızca seçilen alanları alır.
interface Kullanici {
id: number;
ad: string;
email: string;
parolaHash: string;
aktif: boolean;
}
type KullaniciOzeti = Pick<
Kullanici,
"id" | "ad" | "aktif"
>;
const ozet: KullaniciOzeti = {
id: 1,
ad: "Rıfat",
aktif: true,
};Liste ekranında bütün kullanıcı verisine ihtiyaç yoksa daha küçük ve amaca uygun bir tip oluşturulabilir.
Omit<T, K>: Belirli Alanları Çıkarmak
Omit, mevcut tipten belirtilen alanları kaldırır.
type GuvenliKullanici = Omit<
Kullanici,
"parolaHash"
>;
function kullaniciyiGonder(
kullanici: GuvenliKullanici,
) {
return kullanici;
}Bu yaklaşım, hassas alanların API cevabına yanlışlıkla eklenmesini önlemeye yardımcı olur.
Yeni kayıt oluştururken sistem tarafından üretilen alanlar çıkarılabilir:
interface Makale {
id: string;
baslik: string;
icerik: string;
olusturulmaTarihi: Date;
}
type MakaleOlustur = Omit<
Makale,
"id" | "olusturulmaTarihi"
>;Readonly<T>: Alanları Değiştirilemez Yapmak
Readonly<T>, tipin bütün alanlarını salt okunur hâle getirir.
interface SistemAyarlari {
apiUrl: string;
timeout: number;
}
const ayarlar: Readonly<SistemAyarlari> = {
apiUrl: "https://api.example.com",
timeout: 5000,
};
ayarlar.timeout = 10000;
// Hata: Salt okunur alan değiştirilemez.Bu kontrol derleme zamanında geçerlidir. Çalışma zamanında gerçek değişmezlik gerekiyorsa ayrıca uygun JavaScript yöntemleri kullanılmalıdır.
Record<K, T>: Anahtar-Değer Yapısı Oluşturmak
Record, belirli anahtarların aynı türde değerlerle eşleştirildiği nesne tipi oluşturur.
type Rol = "admin" | "editor" | "viewer";
const rolEtiketleri: Record<Rol, string> = {
admin: "Yönetici",
editor: "Editör",
viewer: "Görüntüleyici",
};Union içine yeni bir rol eklendiğinde TypeScript, rolEtiketleri nesnesinin de güncellenmesini ister.
Nesne değerleri daha karmaşık olabilir:
interface RolAyari {
duzenleyebilir: boolean;
silebilir: boolean;
}
const rolAyarlari: Record<Rol, RolAyari> = {
admin: {
duzenleyebilir: true,
silebilir: true,
},
editor: {
duzenleyebilir: true,
silebilir: false,
},
viewer: {
duzenleyebilir: false,
silebilir: false,
},
};Exclude<T, U>: Union İçinden Tip Çıkarmak
Exclude, bir union içinden belirtilen seçenekleri kaldırır.
type Durum =
| "taslak"
| "yayinda"
| "arsivlendi"
| "silindi";
type AktifDurum = Exclude<
Durum,
"silindi"
>;
// "taslak" | "yayinda" | "arsivlendi"İşlem bağlamında kullanılmaması gereken seçenekler bu şekilde çıkarılabilir.
Extract<T, U>: Ortak Tipleri Seçmek
Extract, iki tip arasındaki ortak seçenekleri alır.
type TumOlaylar =
| "click"
| "focus"
| "submit"
| "error";
type FormOlaylari = Extract<
TumOlaylar,
"focus" | "submit" | "change"
>;
// "focus" | "submit"NonNullable<T>: null ve undefined Değerlerini Çıkarmak
type ApiDegeri =
| string
| null
| undefined;
type GecerliDeger = NonNullable<ApiDegeri>;
// stringBir değer doğrulandıktan sonraki durumu modellemek için kullanılabilir.
function degerVarMi<T>(
deger: T,
): deger is NonNullable<T> {
return deger !== null && deger !== undefined;
}
const degerler = [
"TypeScript",
null,
"Node.js",
undefined,
];
const gecerliDegerler = degerler.filter(degerVarMi);
// string[]ReturnType<T>: Fonksiyonun Dönüş Tipini Almak
Bir fonksiyonun dönüş tipini tekrar yazmak yerine ReturnType kullanılabilir.
function kullaniciOlustur() {
return {
id: 1,
ad: "Rıfat",
aktif: true,
};
}
type Kullanici = ReturnType<
typeof kullaniciOlustur
>;Fonksiyonun dönüş yapısı değiştiğinde tip de otomatik olarak güncellenir.
Ancak iş modelinin temel tipi yalnızca bir fonksiyonun mevcut uygulamasına bağlıysa bu yapı gereksiz bağımlılık oluşturabilir. ReturnType, özellikle yardımcı ve fabrika fonksiyonlarında değerlidir.
Parameters<T>: Fonksiyon Parametrelerini Almak
Parameters, bir fonksiyonun parametrelerini tuple olarak verir.
function kullaniciKaydet(
ad: string,
yas: number,
aktif: boolean,
) {
// Kayıt işlemi
}
type KayitParametreleri = Parameters<
typeof kullaniciKaydet
>;
// [ad: string, yas: number, aktif: boolean]Bir fonksiyonu saran yardımcı fonksiyonlarda parametre tiplerini tekrar etmeden korumak için kullanılabilir.
Awaited<T>: Promise Sonucunu Elde Etmek
Awaited, Promise çözüldüğünde elde edilen tipi verir.
async function kullaniciGetir() {
return {
id: 1,
ad: "Rıfat",
};
}
type Kullanici = Awaited<
ReturnType<typeof kullaniciGetir>
>;
// { id: number; ad: string }Asenkron fonksiyonların gerçek veri tipini elde etmek için ReturnType ile birlikte kullanılabilir.
Utility Type’lar Birlikte Nasıl Kullanılır?
Utility type’ların gücü, birlikte kullanıldıklarında daha belirgin hâle gelir.
Örnek bir kullanıcı modeli:
interface Kullanici {
id: string;
ad: string;
email: string;
rol: "admin" | "editor" | "viewer";
parolaHash: string;
olusturulmaTarihi: Date;
guncellenmeTarihi: Date;
}Yeni kullanıcı oluşturma tipi:
type YeniKullanici = Omit<
Kullanici,
| "id"
| "parolaHash"
| "olusturulmaTarihi"
| "guncellenmeTarihi"
>;Kullanıcı güncelleme tipi:
type KullaniciGuncelleme = Partial<
Pick<Kullanici, "ad" | "email" | "rol">
>;Dışarıya gönderilecek güvenli kullanıcı tipi:
type GuvenliKullanici = Omit<
Kullanici,
"parolaHash"
>;Liste görünümü:
type KullaniciListeOgesi = Pick<
Kullanici,
"id" | "ad" | "email" | "rol"
>;Bu tiplerin tamamı ana Kullanici modelinden türetilir. Ana model değiştiğinde ilişkili tiplerin uyumluluğu TypeScript tarafından kontrol edilir.
Union, Generic ve Utility Type Birlikte Kullanımı
Şimdi üç yaklaşımı tek bir API modelinde birleştirelim.
type ApiSonucu<T> =
| {
durum: "basarili";
veri: T;
}
| {
durum: "hatali";
hata: {
kod: string;
mesaj: string;
};
};
interface Urun {
id: string;
ad: string;
fiyat: number;
stok: number;
olusturulmaTarihi: Date;
}
type UrunOlustur = Omit<
Urun,
"id" | "olusturulmaTarihi"
>;
type UrunGuncelle = Partial<UrunOlustur>;
async function urunOlustur(
veri: UrunOlustur,
): Promise<ApiSonucu<Urun>> {
try {
const urun: Urun = {
id: crypto.randomUUID(),
olusturulmaTarihi: new Date(),
...veri,
};
return {
durum: "basarili",
veri: urun,
};
} catch {
return {
durum: "hatali",
hata: {
kod: "URUN_OLUSTURULAMADI",
mesaj: "Ürün oluşturulamadı.",
},
};
}
}Bu örnekte:
- Union type başarılı ve hatalı sonucu ayırır.
- Generic yapı aynı sonuç modelinin farklı verilerle kullanılmasını sağlar.
Omitsistem tarafından oluşturulan alanları giriş tipinden çıkarır.Partialgüncellemede alanların isteğe bağlı gönderilmesini sağlar.
Sonuç iş kurallarını daha açık ifade eden ve yanlış durumları sınırlandıran bir tip modelidir.
Form Durumunu Sağlam Biçimde Modellemek
Formlar, union ve utility type kullanımının faydalı olduğu alanlardan biridir.
interface KayitFormu {
ad: string;
email: string;
parola: string;
}
type FormHatalari<T> = Partial<
Record<keyof T, string>
>;
type FormDurumu<T> =
| {
durum: "duzenleniyor";
degerler: T;
hatalar: FormHatalari<T>;
}
| {
durum: "gonderiliyor";
degerler: T;
}
| {
durum: "basarili";
sonucId: string;
}
| {
durum: "hatali";
degerler: T;
genelHata: string;
};Kullanım:
const durum: FormDurumu<KayitFormu> = {
durum: "duzenleniyor",
degerler: {
ad: "",
email: "",
parola: "",
},
hatalar: {
email: "Geçerli bir e-posta girin.",
},
};FormHatalari<T> yalnızca formda gerçekten bulunan alan adlarını kabul eder. Yanlış bir alan adı yazıldığında TypeScript hata verir.
Tipleri Gereğinden Fazla Karmaşıklaştırmayın
TypeScript güçlü tip araçları sunar ancak her yapının mümkün olan en soyut hâle getirilmesi gerekmez.
Aşağıdaki durumlarda daha basit bir tip daha iyi olabilir:
- Tip yalnızca bir yerde kullanılıyorsa
- Generic yapı okunabilirliği belirgin biçimde azaltıyorsa
- Utility type zinciri ana modeli anlamayı zorlaştırıyorsa
- İş kuralı doğrudan bir interface ile daha açık ifade edilebiliyorsa
- Tip sistemi çalışma zamanı doğrulamasının yerine kullanılmaya çalışılıyorsa
Amaç en kısa veya en “zekice” tipi yazmak değil, hatalı durumları önleyen ve ekip tarafından kolayca anlaşılabilen modeller oluşturmaktır.
Sık Yapılan Hatalar
Her yerde geniş union kullanmak
type Deger =
| string
| number
| boolean
| object
| null;Bu yapı çoğu durumda any kullanımına yakın bir belirsizlik oluşturur. Yalnızca iş kuralında gerçekten geçerli olan seçenekler tanımlanmalıdır.
Generic kullanıp tip ilişkisi kurmamak
function isle<T>(deger: T): string {
return String(deger);
}Burada dönüş tipi her zaman string olduğu için generic gerekli olmayabilir:
function isle(deger: unknown): string {
return String(deger);
}Generic, tip parametresi giriş, çıkış veya birden fazla alan arasında anlamlı ilişki kuruyorsa kullanılmalıdır.
Her güncellemede doğrudan Partial<Model> kullanmak
Partial<Kullanici> sistem tarafından yönetilen id gibi alanları da opsiyonel ve güncellenebilir gösterir. Önce izin verilen alanları Pick ile seçmek daha güvenlidir.
type KullaniciGuncelleme = Partial<
Pick<Kullanici, "ad" | "email">
>;Tipleri çalışma zamanı doğrulaması sanmak
const veri = await yanit.json() as Kullanici;Type assertion gelen veriyi doğrulamaz. Dış veri çalışma zamanında kontrol edilmelidir.
Çok fazla tip iddiası kullanmak
Sık sık as kullanma ihtiyacı, modelin gerçek veriyi doğru yansıtmadığını gösterebilir. Öncelikle union, type guard ve generic constraint seçenekleri değerlendirilmelidir.
Readonly ile tam değişmezlik beklemek
Readonly<T> yalnızca üst seviyedeki alanları salt okunur yapar ve derleme zamanında kontrol sağlar. İç içe nesneler için ayrıca derin readonly modeli gerekebilir.
Hangi Yapıyı Ne Zaman Kullanmalıyım?
| İhtiyaç | Kullanılabilecek Yapı |
|---|---|
| Bir değer birkaç farklı tip alabilir | Union type |
| Seçenekleri belirli değerlerle sınırlandırmak | Literal union |
| Duruma göre nesne yapısı değişiyor | Discriminated union |
| Aynı kod farklı tiplerle çalışıyor | Generic |
| Generic tipi belirli yapılarla sınırlandırmak | Generic constraint |
| Nesnenin güvenli anahtarlarını kullanmak | keyof |
| Bütün alanları opsiyonel yapmak | Partial |
| Bütün alanları zorunlu yapmak | Required |
| Belirli alanları seçmek | Pick |
| Belirli alanları çıkarmak | Omit |
| Alanları salt okunur yapmak | Readonly |
| Anahtar-değer haritası oluşturmak | Record |
| Union içinden seçenek çıkarmak | Exclude |
null ve undefined değerlerini çıkarmak | NonNullable |
| Fonksiyonun dönüş tipini almak | ReturnType |
| Asenkron sonucu elde etmek | Awaited |
Daha Sağlam Tipler İçin Pratik İlkeler
İş kurallarını tiplerle ifade edin
Bir sipariş hem “iptal” hem “teslim edildi” durumunda olamıyorsa tip modeli de buna izin vermemelidir.
İmkânsız durumları modellenemez hâle getirin
Çok sayıda opsiyonel alana sahip tek bir interface yerine discriminated union kullanmayı değerlendirin.
Tip bilgisini koruyun
Giriş ve çıkış arasında ilişki varsa any yerine generic kullanın.
Ana tipleri kopyalamayın
Yeni tipler oluştururken uygun durumlarda Pick, Omit ve Partial kullanın.
Dış veriyi doğrulayın
TypeScript tipleri çalışma zamanında bulunmaz. API, dosya ve kullanıcı girdileri uygulamaya alınmadan önce doğrulanmalıdır.
Okunabilirliği önceliklendirin
Karmaşık tipleri anlamlı isimlere ayırın:
type DuzenlenebilirAlanlar = Pick<
Kullanici,
"ad" | "email"
>;
type KullaniciGuncelleme =
Partial<DuzenlenebilirAlanlar>;Bu kullanım, uzun bir utility type zincirini her yerde tekrar etmekten daha anlaşılırdır.
Sonuç
Union, generic ve utility type yapıları TypeScript’in en güçlü araçları arasındadır. Doğru kullanıldıklarında yalnızca kod tamamlama sağlamaz; iş kurallarını doğrudan tip sistemine taşır ve hatalı durumların oluşmasını zorlaştırır.
Temel yaklaşımı şöyle özetleyebiliriz:
- Birden fazla geçerli seçenek için union type kullanın.
- Duruma göre değişen nesneleri discriminated union ile modelleyin.
- Tip bilgisini kaybetmeden tekrar kullanılabilir kod yazmak için generic kullanın.
- Generic yapıların sınırlarını constraint ile belirleyin.
- Mevcut tiplerden yeni modeller üretmek için utility type’lardan yararlanın.
- Dış verileri çalışma zamanında doğrulamayı unutmayın.
- Tipleri gereksiz yere karmaşıklaştırmadan gerçek iş kurallarını yansıtın.
Sağlam TypeScript kodu, her değişkene mümkün olan en ayrıntılı tipi yazmak değildir. Önemli olan geçerli durumları açık, geçersiz durumları ise mümkün olduğunca oluşturulamaz hâle getirmektir.
Sıkça Sorulan Sorular
TypeScript union type nedir?
Union type, bir değişkenin tanımlanan birden fazla tipten birini alabilmesini sağlar. Tipler | karakteriyle birleştirilir.
Generic ile any arasındaki fark nedir?
any tip bilgisini ve kontrolleri ortadan kaldırır. Generic ise farklı tiplerle çalışırken giriş ve çıkış arasındaki tip ilişkisini korur.
Utility type nedir?
Utility type, mevcut bir TypeScript tipinden yeni tipler oluşturmayı sağlayan hazır yardımcı yapılardır. Partial, Pick, Omit, Readonly ve Record sık kullanılan örneklerdir.
Partial ve Pick arasındaki fark nedir?
Partial, bir tipteki bütün alanları opsiyonel yapar. Pick ise yalnızca belirtilen alanları seçerek yeni bir tip oluşturur.
Omit ne zaman kullanılmalıdır?
Bir modeldeki bazı alanların yeni tipte bulunmaması gerektiğinde kullanılır. Örneğin parola bilgisini API yanıtından veya sistem tarafından oluşturulan id alanını kayıt girdisinden çıkarmak için tercih edilebilir.
Generic her fonksiyonda kullanılmalı mı?
Hayır. Generic, tip parametresi giriş ve çıkış arasında anlamlı bir ilişki kuruyorsa kullanılmalıdır. Sabit bir tip yeterliyse daha basit tanım tercih edilmelidir.