TypeScript'te ayrı bir dictionary ya da hashmap class'ı yoktur. Bir dictionary ya index signature ile tiplenmiş düz bir nesnedir, { [key: string]: number }, ya aynı tipin Record<string, number> yazımıdır ya da bir Map<string, number>'dır. Üçü de değerleri anahtara göre saklar; anahtar tiplerinde, eksik anahtarların nasıl tiplendiğinde ve nasıl serileştirildiklerinde ayrışırlar.
String anahtarlar ve JSON biçimli veri için Record<string, T> ile tiplenmiş bir nesne olağan seçimdir. String olmayan anahtarlar ya da sürekli eklenip silinen girdiler için Map kullanın.
Index Signature
Bir index signature, [key: KeyType]: ValueType, "bu tipteki her anahtar bu tipte bir değere karşılık gelir" der. Anahtarın adı (key, name, userId) yalnızca belgeleme içindir. Anahtar tipi string, number, symbol, template literal kalıpları ya da bunların union'ı olabilir.
JavaScript number anahtarları string'e çevirir; bu yüzden bir { [id: number]: string } nesnesinin anahtarları çalışma zamanında yine string'dir: Object.keys({ 1: "one" }) sonucu [ '1' ] olur. Number index signature yalnızca TypeScript'te nesneye nasıl erişebileceğinizi kısıtlar.
Record<K, V>
Record<string, V>, { [key: string]: V } için bir kısaltmadır. string yerine literal anahtarlardan oluşan bir union verildiğinde, her anahtarı içermek zorunda olan sabit bir dictionary'ye dönüşür:
urls içinden staging anahtarını çıkarmak TS2741 derleme hatasıdır (Property 'staging' is missing...); bu da union anahtarlı Record'u denetlenen bir arama tablosu yapar. Partial her değeri number | undefined yapar.
Hash Map Olarak Map
Bir Map her tipte anahtarı kabul eder, ekleme sırasını korur, bir size değerine sahiptir ve eksik anahtarları dürüstçe tipler: get, V | undefined döndürür.
Bir Anahtarın Var Olup Olmadığını Kontrol Etmek
Birkaç kontrol yolu vardır ve hepsi aynı anlama gelmez:
| Kontrol | Nerede çalışır | Dikkat edilecek nokta |
|---|---|---|
Object.hasOwn(obj, key) | nesneler | ES2022; eski hedeflerde Object.prototype.hasOwnProperty.call(obj, key) kullanın |
key in obj | nesneler | toString ve constructor gibi miras alınan anahtarlar için de true |
obj[key] !== undefined | nesneler | Eksik bir anahtarı undefined olarak saklanmış bir anahtardan ayıramaz |
if (obj[key]) | nesneler | 0, "" ve false değerleri için de false |
map.has(key) | Map | Sonraki bir map.get(key) çağrısını daraltmaz |
map.get(key) !== undefined | Map | Nesnelerdeki undefined uyarısının aynısı |
Miras alınan anahtar sorunu, kullanıcıdan gelen "constructor" ya da "__proto__" gibi anahtarların düz nesneleri dictionary olarak riskli yapmasının nedenidir. Bir Map böyle anahtarlara sahip değildir.
Eksik Anahtarın Tip Sorunu
Bir index signature ya da Record<string, T> üzerinde herhangi bir anahtarı okumanın tipi T'dir, var olmayan bir anahtar için bile. Derleyici, çalışma zamanında undefined olan bir değer üzerinde metot çağırmanıza izin verir:
noUncheckedIndexedAccess derleyici seçeneği bunu düzeltir: bu seçenekle colors["grass"] ifadesinin tipi string | undefined olur ve siz kontrol edene kadar toUpperCase çağrısı bir derleme hatasıdır. strict kapsamında değildir, bu yüzden tsconfig.json içinde ayrıca açılması gerekir; yanında duran diğer bayraklar için strict mode sayfasına bakın. Bir Map'te böyle bir boşluk yoktur, çünkü get her zaman undefined içerir.
Ekleme, Silme ve Döngü
delete, index signature özelliklerinde çalışır. Bir nesne tipinin zorunlu adlandırılmış bir özelliğinde ise TS2790 derleme hatasıdır: The operand of a 'delete' operator must be optional.
Hangisini Kullanmalı
| İhtiyaç | Kullanın |
|---|---|
| String anahtarlar, JSON girdi ya da çıktı | Record<string, T> |
| Sabit, bilinen ve hepsi zorunlu bir anahtar kümesi | Record<"a" | "b", T> |
| Adlandırılmış özellikler artı rastgele ek anahtarlar | index signature içeren bir nesne tipi |
| Nesne olan anahtarlar, number olarak kalması gereken sayılar ya da string olmayan her şey | Map<K, V> |
| Sık ekleme ve silme ya da sık okunan bir boyut | Map<K, V> |
| Kullanıcıdan gelen anahtarlar | Map<K, V> (miras alınan anahtar yok) |
Sıkça Sorulan Sorular
TypeScript'te nasıl dictionary oluştururum?
Düz bir nesneyi index signature ile tipleyin, const ages: { [name: string]: number } = {}, ya da eşdeğeri olan Record<string, number> ile. Sonra girdileri ages["ada"] = 36 ile ekleyin. String olmayan anahtarlar ya da sık sık ekleme ve silme yapılan bir koleksiyon için new Map<string, number>() kullanın.
TypeScript'te HashMap var mı?
Bu adla yok. JavaScript'in yerleşik Map class'ı bir hash map'tir: Map<K, V> anahtar-değer çiftlerini anahtarla hızlı erişim için saklar, ekleme sırasını korur ve her anahtar tipini kabul eder. String anahtarlar için diğer yaygın seçenek Record<string, V> olarak tiplenmiş düz bir nesnedir.
TypeScript dictionary'sinde bir anahtarın var olup olmadığını nasıl kontrol ederim?
Nesne dictionary'si için Object.hasOwn(dict, key) ya da key in dict kullanın (ikincisi toString gibi miras alınan özellikleri de görür) veya değeri okuyup undefined ile karşılaştırın. Bir Map için map.has(key) kullanın ya da map.get(key) sonucunu doğrudan kontrol edin, çünkü has sonraki bir get çağrısını daraltmaz.
Index signature ile Record arasındaki fark nedir?
{ [key: string]: T } ve Record<string, T> aynı tipi tanımlar. Record daha kısadır ve belirli anahtarlardan oluşan bir union da alabilir, Record<"a" | "b", T>, bu durumda her anahtar zorunludur. Index signature ise tek bir nesne tipinde adlandırılmış özelliklerle birleştirilebilir ve anahtarı onu belgeleyen bir ad taşıyabilir, örneğin { [userId: string]: User }.
Eksik bir dictionary anahtarını okumak neden hata vermiyor?
Varsayılan olarak bir index signature ya da Record<string, T> üzerinde dict[key] ifadesinin tipi T'dir, oysa eksik bir anahtar için değer çalışma zamanında undefined olur. tsconfig.json içinde noUncheckedIndexedAccess seçeneğini açarsanız tip T | undefined olur ve kontrol yapmanız zorunlu hale gelir. strict bu seçeneği içermez.