Bir nesnenin biçimini tanımlamak için interface ve type aynı işi yapar ve biçimler eşleştiğinde birinin değerleri diğerine atanabilir. Farklar kenarlardadır: type, bir interface'in ad veremediği şeylere (union'lar, tuple'lar, hesaplanan tipler) ad verebilir; bir interface de bir type alias'ın yapamadığı birkaç şeyi (birleşme, kontrollü extends) yapabilir.
TypeScript nesne tiplerini yapılarına göre karşılaştırır, bu yüzden atanabilirlik açısından bildirimin adı önemli değildir. Aşağıda seçimin önemli olduğu yerlerin listesi var.
Karşılaştırma Tablosu
| Özellik | interface | type |
|---|---|---|
| Nesne biçimleri | evet | evet |
| Opsiyonel, readonly, metotlar, index signature'lar | evet | evet |
| Generic'ler | evet | evet |
Union'lar (A | B) | hayır | evet |
| Tek başına tuple'lar, primitive'ler, fonksiyon tipleri | hayır (fonksiyon tipleri yalnızca call signature olarak) | evet |
| Mapped ve conditional tipler | hayır | evet |
| Genişletme | extends, çakışmalar hatadır | &, çakışmalar never olur |
| Declaration merging | evet | hayır (duplicate identifier) |
Record<string, T> tipine atanabilir | hayır | evet, özellikler uyuyorsa |
Bir sınıfta implements | evet | evet, bir nesne tipiyse |
| Özyinelemeli tanımlar | evet | evet |
Yalnızca type'ın Yapabildikleri
Tek bir nesne biçimi olmayan her şey bir type alias gerektirir:
Bunların hiçbiri interface ile yazılamaz (son ikisi mapped types ve conditional types sayfalarında anlatılıyor). Nesne biçimleri için ne kullanırsa kullansın her kod tabanının bir yerde type kullanmasının pratik nedeni budur.
Yalnızca interface'in Yapabildiği: Declaration Merging
Aynı kapsamda aynı adı taşıyan iki interface bildirimi tek bir bildirimde birleşir. Aynı adı taşıyan iki type bildirimi ise TS2300 hatasıdır, Duplicate identifier.
interface Settings {
theme: string;
}
interface Settings {
fontSize: number;
}
const s: Settings = { theme: "dark", fontSize: 14 }; // needs both
type Options = { a: number };
type Options = { b: number }; // error TS2300: Duplicate identifier 'Options'.
Kütüphane tipleri dışarıdan birleştirme ile genişletilir: global Window tipine, Express'in Request tipine ya da bir kütüphanenin tema tipine bir özellik eklemek. Kullanıcıların genişletmesi gerekebilecek tipler yayınlıyorsanız interface kullanın. Kendi uygulama kodunuzda kazara bir birleştirme (aynı global interface adını bildiren iki betik dosyası), ikisi aynı özelliği farklı tiplerle bildirmedikçe sessizce gerçekleşir; bu da bazı ekiplerin type tercih etmek için öne sürdüğü bir gerekçedir.
extends ile Intersection
Bir interface extends ile, bir type alias & ile genişler. Çoğunlukla aynı sonucu üretirler ama çakışan bir özelliği farklı ele alırlar. extends çakışmayı bildirimde bildirir:
index.ts(7,11): error TS2430: Interface 'Broken' incorrectly extends interface 'Base'.
Types of property 'id' are incompatible.
Type 'number' is not assignable to type 'string'.
Bir intersection aynı çakışmayı sessizce kabul eder ve özelliği never tipine çevirir (aynı zamanda bir number olan bir string). Hata ancak daha sonra, bir değer oluşturmaya çalıştığınızda görünür:
Hata, ona neden olan bildirimden uzakta, nesneyi gösterir. Nesne tiplerini başka nesne tiplerinden oluştururken extends daha iyi bir mesaj verir.
Index Signature'lar: Gözden Kaçması Kolay Bir Fark
Bir nesne tipi için type alias örtük bir index signature alır, bu yüzden bir Record<string, unknown> beklenen yere verilebilir. Bir interface almaz. Bu tasarım gereğidir: TypeScript ekibinin açıklamasına göre (TypeScript deposundaki #15300 numaralı issue) bir interface sonraki bildirimlerle genişletilebilir, bu yüzden onun için bir index signature çıkarmak daha az güvenlidir ve kuralı şimdi değiştirmek çok fazla kodu bozardı.
@ts-expect-error ile işaretlenen çağrı yine çalışır ve Alan'ın alanlarını yazdırır: kural derleme zamanına aittir. Bir interface değeri Record<string, ...> ile tiplenmiş bir loglama, serileştirme veya sorgu yardımcısına verildiğinde alınan kafa karıştırıcı hatanın alışılmış nedeni budur. O tek bildirimi type yapın, değeri yayın (spread) ya da yardımcının parametresini bir interface veya bir generic ile tipleyin.
Performans ve Hata Mesajları
TypeScript wiki'sinin Performance sayfası ("Preferring Interfaces Over Intersections" bölümü), nesne tiplerini birleştirirken type Foo = Bar & Baz & { ... } yerine interface Foo extends Bar, Baz { ... } önerir. Gerekçeleri: bir interface, özellik çakışmalarını algılayan tek ve düz bir nesne tipidir; interface'ler arasındaki tip ilişkileri önbelleğe alınır (intersection'ların bütünü alınmaz); ve bir değeri bir intersection'a karşı kontrol etmek, düzleştirilmiş tipten önce her bileşeni kontrol eder. Fark, çok sayıda birleştirilmiş tip içeren büyük kod tabanlarında önemlidir; birkaç sıradan nesne biçimi için ölçemezsiniz.
Aynı sayfa interface'lerin daha iyi göründüğünü de belirtir. Bir interface üzerine gelince açılan ipuçlarında ve hata mesajlarında adıyla gösterilir; bir intersection alias'ı ise çoğu zaman parçalarına açılmış halde yazdırılır, bu da uzun mesajları okumayı zorlaştırır.
Hangisini Kullanmalı
İşe yarayan bir kural:
- Nesne biçimleri:
interface. Kontrollüextends, büyük birleşimlerde daha anlaşılır hatalar sağlar ve kütüphane kullanıcılarının onu genişletmesine izin verir. Bu, TypeScript handbook'unun pratik kuralıyla örtüşür: "typeözelliklerine ihtiyaç duyana kadarinterfacekullanın". - Geri kalan her şey:
type. Union'lar, tuple'lar, fonksiyon tipleri, literal tipler ve mapped, conditional veya template literal tiplerle oluşturulan her şey. - İstisna:
Record<string, ...>parametrelerine uyması gereken bir nesne biçimi için ya da birleştirmeyi bilerek engellemek istediğinizdetypekullanın.
Her şey için type kullanmak da tutarlı bir seçimdir ve pek çok kod tabanı bunu yapar. Kaçınılması gereken tek seçenek ikisini gelişigüzel karıştırmaktır: okuyucuların bir farkın kasıtlı olup olmadığını merak etmesine yol açar.
Sıkça Sorulan Sorular
TypeScript'te type ile interface arasındaki fark nedir?
İkisi de nesne biçimlerini tanımlar ve bunun için birbirinin yerine geçer. type ayrıca union'lara, tuple'lara, primitive'lere ve mapped veya conditional tiplere ad verebilir; interface bunu yapamaz. interface declaration merging'i ve çakışan özellikleri kontrol eden extends yapısını destekler. type ile yazılan nesne tipleri Record<string, unknown> gibi index signature tiplerine de atanabilir, interface'ler atanamaz.
type mı yoksa interface mi kullanmalıyım?
TypeScript handbook'unun pratik kuralı şudur: yalnızca type tipinin sahip olduğu bir özelliğe ihtiyaç duyana kadar interface kullanın. Pratikte bu, nesne biçimleri için interface'ler, union'lar, tuple'lar, fonksiyon tipleri ve hesaplanan tipler için type demektir. Her şey için type kullanan ekipler de iyi gider; önemli olan tutarlı tek bir kuraldır.
TypeScript'te interface, type'tan daha mı hızlı?
Nesne tiplerini birleştirirken bazı durumlarda evet. TypeScript ekibinin performans rehberi büyük intersection'lar (A & B & { ... }) yerine interface ... extends önerir, çünkü interface'ler arasındaki ilişkiler önbelleğe alınır ve bir interface tek bir düz tiptir. Basit bir nesne biçimi için anlamlı bir fark yoktur.
Bir sınıf bir type alias'ı implement edebilir mi?
Evet, alias bir nesne tipiyse (ya da nesne tiplerinin bir intersection'ıysa): class Point implements PointType { ... } çalışır. Bir sınıf bir union tipini implement edemez; bu TS2422 hatasıdır.
Bir interface bir type alias'ı genişletebilir mi?
Evet, alias bir nesne tipi olduğu sürece: type Base = { id: string }; interface User extends Base { name: string } geçerlidir. Bir union alias'ını genişletemez. Diğer yönde, bir type alias bir interface üzerine & ile kurulabilir.