- Konu sahibi
- #1
İki seçenekten birini her durumda üstün kabul etmek yerine şu pratik kuralı kullanın: Bir nesnenin özelliklerini ve genişletilebilir bir sözleşmeyi tanımlıyorsanız
Her iki tanım da
Burada önemli nokta, iki yazımın da JavaScript’e
Bir model, seçenek nesnesi, sınıfın uygulaması gereken sözleşme veya kütüphane tarafından dışarı açılan nesne yapısı tanımlıyorsanız
Burada
İki
Bu örnekte
Aşağıdaki tablo seçim ölçütünü kısa biçimde ayırır.
Tablo mutlak bir zorunluluk listesi değildir. Bazı nesne türleri hem
Aynı nesne biçimini iki farklı yolla genişletmek mümkün olsa da çakışmaların ele alınma biçimi farklıdır:
Bu iki örnekte de yönetici nesnesi
Yeni bir tür tanımlarken şu sırayı izleyin:
Burada
interface, birleşim, demet, ilkel tür veya koşullu tür gibi daha geniş bir tür ifadesine ihtiyacınız varsa type kullanın. Kararsız kaldığınızda interface ile başlamak genellikle uygundur; kullandığınız yapı interface ile ifade edilemiyorsa type seçin. Bu tercih çalışma zamanındaki JavaScript çıktısını değiştirmez, yalnızca TypeScript’in derleme aşamasındaki tür denetimini ve kodun nasıl genişletileceğini etkiler.Aynı nesne için iki yazım da mümkün olabilir
interface, çoğunlukla bir nesnenin sahip olması gereken özellikleri adlandırılmış bir sözleşme olarak tanımlar. type ise bir tür için yeni bir ad oluşturur. Basit bir nesne söz konusu olduğunda aşağıdaki iki tanım benzer biçimde kullanılabilir:
Kod:
interface User {
id: number;
name: string;
}
type User = {
id: number;
name: string;
};
Her iki tanım da
id alanı sayı, name alanı dize olan bir nesneyi kabul eder. Örneğin const user: User = { id: 1, name: "Ayşe" }; ifadesi, ilgili tanım kullanıldığında geçerlidir. Eksik veya yanlış türde bir alan verilirse TypeScript hata bildirir.Burada önemli nokta, iki yazımın da JavaScript’e
interface veya type adıyla bir nesne üretmemesidir. Bunlar çalışma zamanında var olan sınıflar ya da doğrulama mekanizmaları değildir. Derleme sırasında tür kontrolü sağlarlar; dışarıdan gelen JSON verisinin gerçekten bu yapıya sahip olduğunu kendiliğinden kanıtlamazlar. API yanıtı gibi güvenilmeyen veriler için ayrıca çalışma zamanı doğrulaması gerekir.Nesne sözleşmelerinde neden interface tercih edilir?
Bir model, seçenek nesnesi, sınıfın uygulaması gereken sözleşme veya kütüphane tarafından dışarı açılan nesne yapısı tanımlıyorsanız
interface daha anlaşılır bir başlangıçtır:
Kod:
interface Product {
id: number;
title: string;
price: number;
}
interface DiscountedProduct extends Product {
discountRate: number;
}
Burada
DiscountedProduct, Product içindeki alanları devralır ve kendi alanını ekler. Bu kullanım, modelin temel yapısı ile ondan türeyen yapı arasındaki ilişkiyi doğrudan gösterir. Bir interface birden fazla interface’i de genişletebilir.interface ayrıca aynı ada sahip bildirimlerin birleştirilebilmesine izin verir. Bu davranışa declaration merging, yani bildirim birleştirme denir:
Kod:
interface AppConfig {
apiUrl: string;
}
interface AppConfig {
timeout: number;
}
const config: AppConfig = {
apiUrl: "/api",
timeout: 5000,
};
İki
AppConfig bildirimi tek bir yapı gibi değerlendirilir. Bu özellik, özellikle kütüphane türlerini genişletmek veya global nesnelere TypeScript türü eklemek gerektiğinde kullanılabilir. Ancak uygulama kodunda aynı adı farklı dosyalarda farkında olmadan yeniden tanımlamak kafa karışıklığına yol açabileceği için bildirim birleştirmeyi yalnızca bilinçli bir tasarım kararı olduğunda kullanın.type hangi durumlarda doğru seçimdir?
type, tek başına bir nesne yapısından daha fazlasını adlandırabilir. Birleşim türü, demet, ilkel tür için takma ad, fonksiyon türü veya koşullu tür yazmanız gerektiğinde genellikle type kullanılır.
Kod:
type Status = "pending" | "success" | "error";
type Point = [x: number, y: number];
type Identifier = string | number;
type Handler = (message: string) => void;
Bu örnekte
Status yalnızca üç belirli dizeden birini, Point iki elemanlı bir demeti, Identifier dize veya sayıyı, Handler ise belirli imzaya sahip bir fonksiyonu ifade eder. Bu yapıların tamamını interface ile aynı açıklıkta tanımlayamazsınız; özellikle birleşim ve demet türlerinde type gerekli hale gelir.type, nesne türlerini birleştirmek için kesişim türlerini de kullanır:
Kod:
type HasId = {
id: number;
};
type HasTimestamps = {
createdAt: Date;
updatedAt: Date;
};
type StoredEntity = HasId & HasTimestamps;
StoredEntity değerinin her iki yapının alanlarını da taşıması gerekir. Aynı amaç interface extends ile de ifade edilebiliyorsa iki yaklaşımın anlamı tamamen aynı değildir. Interface’lerde çakışan özellikler uyumsuzsa tanım sırasında hata alınır. Kesişim türlerinde ise aynı ada sahip farklı türler birlikte istenebilir ve sonuç kullanılamaz bir tür hâline gelebilir. Bu nedenle iki yapıyı nesne sözleşmesi olarak bilinçli biçimde genişletiyorsanız extends, tür ifadelerini birleştirmeniz gerekiyorsa & kullanın.Aşağıdaki tablo seçim ölçütünü kısa biçimde ayırır.
| İhtiyaç | Tercih | Neden |
|---|---|---|
| Nesne özelliklerini adlandırmak | interface | Nesne sözleşmesini açıkça ifade eder ve genişletilebilir. |
| Birleşim türü yazmak | type | A | B biçimini doğrudan destekler. |
| Demet tanımlamak | type | Eleman sırasını ve türlerini adlandırabilir. |
| İlkel tür için takma ad oluşturmak | type | string, number veya benzeri türleri adlandırabilir. |
| Fonksiyon türü tanımlamak | Genellikle type | Fonksiyon imzasını kısa ve doğrudan ifade eder. |
| Bir yapının sonradan bildirimle genişletilmesi | interface | Declaration merging kullanılabilir. |
| Sınıfın uygulaması gereken nesne sözleşmesi | interface | implements ile sınıfın beklenen üyeleri denetlenebilir. |
interface hem type ile yazılabilir. Bu durumda ekipte kullanılan kodlama kuralı, türün ileride genişletilip genişletilmeyeceği ve tanımın okuyucuya en açık biçimi belirleyici olmalıdır.extends ile kesişim türü arasındaki farkı kontrol edin
Aynı nesne biçimini iki farklı yolla genişletmek mümkün olsa da çakışmaların ele alınma biçimi farklıdır:
Kod:
interface User {
id: number;
}
interface Admin extends User {
permissions: string[];
}
// Eşdeğer amaç için type yaklaşımı
type UserByType = {
id: number;
};
type AdminByType = UserByType & {
permissions: string[];
};
Bu iki örnekte de yönetici nesnesi
id ve permissions alanlarını taşır. Ancak temel türlerde aynı özellik farklı türlerle tanımlanırsa interface bildirimi uyumsuzluğu erken bildirir. Kesişim türü ise iki koşulu aynı anda karşılamaya çalışır. Örneğin string & number pratikte hiçbir normal değerin sağlayamayacağı bir türdür. Bu nedenle çakışmayı derleme sırasında daha görünür kılmak istediğiniz nesne hiyerarşilerinde interface extends daha güvenli ve okunabilir olabilir.Günlük kullanım için uygulanabilir karar kuralı
Yeni bir tür tanımlarken şu sırayı izleyin:
- Tanımladığınız şey bir nesnenin özellikleri, bir API sözleşmesi veya bir sınıfın uygulayacağı yapıysa önce
interfaceyazın. - Tür bir birleşim (
A | B), demet, ilkel tür takma adı, fonksiyon imzası veya koşullu tür içeriyorsatypekullanın. - Bir nesne sözleşmesini başka nesne sözleşmeleriyle açıkça genişletiyorsanız
interface extendsseçin. - Aynı nesne türlerini bir ifade içinde birleştirmeniz gerekiyorsa
typeve&kullanın. - Kütüphane veya global türleri sonradan genişletme ihtiyacı varsa declaration merging gerekip gerekmediğini kontrol edin; gerekiyorsa
interfaceseçin. - Tanım hem interface hem type ile yazılabiliyorsa, ekibin mevcut kuralını ve okuma kolaylığını koruyun; yalnızca biçimsel bir üstünlük aramayın.
Kod:
interface User {
id: number;
name: string;
}
type UserState = "active" | "blocked" | "pending";
type UserRecord = User & {
state: UserState;
};
Burada
User nesnenin temel sözleşmesidir. UserState, yalnızca izin verilen durumları sınırlar. UserRecord ise iki türü birleştirir. Sonuç olarak UserRecord kullanılırken id, name ve state zorunludur; state alanı da belirtilen üç değerden biri olmalıdır. Bu ayrım, her şeyi tek bir büyük interface içine koymaktan daha anlaşılırdır çünkü nesne yapısı ile değer kümesini farklı amaçlarla ifade eder.Sık sorulan sorular
interface type’tan daha hızlı mıdır?
Bazı interface genişletme biçimleri, kesişim türleriyle kurulan eşdeğer yapılara göre derleyici açısından daha uygun olabilir; ancak küçük bir projede yalnızca bu gerekçeyle tüm
type tanımlarını değiştirmek doğru değildir. Önceliği türün anlamına, okunabilirliğine ve proje kuralına verin. Gerçek bir derleme sorunu varsa proje kodu ve TypeScript sürümü birlikte incelenmelidir.Bir sınıf için interface mi type mı kullanılmalı?
Sınıfın uygulaması gereken herkese açık üyeleri bir sözleşme olarak tanımlıyorsanız
interface doğal seçimdir ve sınıf bunu implements ile uygulayabilir. Sözleşme bir birleşim, koşullu tür veya başka bir tür ifadesi içeriyorsa type kullanılabilir; ancak sınıfın implements ifadesinin gerçekten beklediğiniz yapıyı kabul ettiğini derleyiciyle kontrol etmelisiniz.interface ile bir union type yazılabilir mi?
Hayır.
interface doğrudan "draft" | "published" gibi bir birleşim türünü tanımlamak için kullanılmaz. Bu durumda type Status = "draft" | "published"; yazmalısınız. Birleşim üyelerinin her biri nesne ise bu nesneleri interface olarak tanımlayıp sonrasında type Result = Success | Failure; biçiminde birleştirebilirsiniz.API’den gelen veriye interface yazmak veriyi doğrular mı?
Hayır.
interface ve type yalnızca TypeScript’in derleme zamanı tür denetimidir; sunucudan gelen JSON’un gerçekten bu yapıya sahip olup olmadığını çalışma zamanında kontrol etmez. Dış veriyi kullanmadan önce alanların varlığını, türünü ve izin verilen değerlerini ayrıca doğrulamanız gerekir.