Record<K, V>, anahtarları K tipinde ve değerlerinin hepsi V tipinde olan bir nesne için yerleşik bir utility type'tır. string anahtarlarla bir sözlüğü tanımlar; literal anahtarlardan oluşan bir union ile tam olarak bu anahtarlara sahip olması gereken bir nesneyi tanımlar.
Record yalnızca tip sisteminde vardır. Çalışma zamanında iki nesne de düz JavaScript nesneleridir; bu yüzden nesne literal'leri, spread, JSON.stringify ve nesne alan diğer her şeyle çalışırlar.
Sözdizimi ve Tanım
Record<Keys, Value>
Keys bir nesne anahtarı olabilecek bir şey olmalıdır: string, number, symbol, string ya da number literal'lerinden oluşan bir union veya bir template literal tipi. Value herhangi bir tip olabilir. TypeScript'in standart kütüphanesindeki tanımın tamamı tek satırlık bir mapped type'tır:
type Record<K extends keyof any, T> = {
[P in K]: T;
};
keyof any, olası tüm anahtar tiplerinin kümesi olan string | number | symbol tipidir. [P in K]: T, K tipinin her üyesi için T tipinde bir özellik oluşturur. Bu, aşağıdaki iki davranışı da açıklar: string gibi geniş bir K bir index signature üretir (herhangi bir anahtar), bir union K ise her üye için bir zorunlu özellik üretir.
Union Anahtarlar: Her Anahtar Zorunludur
Anahtarlar literal'lerden oluşan bir union olduğunda, bir Record her birini listelemelidir ve başka hiçbirini değil. Bu, derleyiciyi bir kontrol listesine dönüştürür:
Status tipine "cancelled" ekleyin; yeni duruma bir etiket ve bir renk verene kadar iki nesne de derlenmez. Bir anahtarı atlamak ya da union'da olmayan bir anahtar eklemek derleme hatasıdır:
index.ts(4,7): error TS2741: Property 'error' is missing in type '{ idle: string; loading: string; success: string; }' but required in type 'Record<Status, string>'.
index.ts(11,54): error TS2353: Object literal may only specify known properties, and 'paused' does not exist in type 'Record<Status, string>'.
Aynısı anahtar tipi olarak bir string enum ile de çalışır: Record<Color, string> her enum üyesi için bir girdi ister.
Record<string, T> ve Eksik Anahtar
string anahtarlarla her anahtara izin verilir ve TypeScript her okumayı, orada olmayan bir anahtar için bile, T olarak tipler. Çalışma zamanında eksik bir anahtar undefined verir:
Bu en yaygın Record hatasıdır. Bunu ele almanın üç yolu var: okumadan önce in ya da Object.hasOwn ile kontrol edin, değeri V | undefined olarak bildirin ya da projedeki her index signature okumasına | undefined ekleyen noUncheckedIndexedAccess derleyici seçeneğini açın. Union anahtarlı bir Record'da bu sorun yoktur, çünkü her anahtarın var olması garantidir.
Partial<Record<K, V>>: Yalnızca Bazı Anahtarlar
Anahtarlar için bir union kullanıp hepsini zorunlu kılmamak için Record'u Partial ile sarın. Okumalar o zaman V | undefined döndürür, bu da dürüst bir tiptir:
jp gibi yanlış yazılmış bir anahtar yine hatadır; Record<string, string> tipine göre avantajı budur.
Bir Record Üzerinde Döngü
Object.keys, Object.values ve Object.entries hepsi çalışır. Sorun anahtar tipindedir: Object.keys string[], Object.entries de [string, V][] döndürür; asla sizin anahtar union'ınızı döndürmez:
TypeScript anahtarları bilerek string olarak tutar: bir nesnenin çalışma zamanında tipinin listelediğinden daha fazla özelliği olabilir, dolayısıyla genel olarak Plan[] sözü vermek güvensiz olurdu. seats gibi bir literal'den oluşturduğunuz bir nesne için cast güvenlidir.
Veriden Bir Record Oluşturmak
Record'lar bir diziyi gruplamanın ya da indekslemenin olağan sonucudur. Record tipinde boş bir nesneyle başlayın ve onu doldurun:
Book["genre"], interface'teki union'ı anahtar tipi olarak yeniden kullanır; böylece Book tipine bir tür eklemek, byGenre nesnesinin yeni bir girdi istemesine yol açar.
Record<string, unknown> ve Interface'ler
Record<string, unknown>, "string anahtarlı bir nesne" için yaygın bir tiptir. Nesne literal'lerini ve bir type alias ile tiplenmiş değerleri kabul eder, ancak bir interface reddedilir:
index.ts(11,11): error TS2345: Argument of type 'User' is not assignable to parameter of type 'Record<string, unknown>'.
Index signature for type 'string' is missing in type 'User'.
Interface'ler declaration merging ile genişletilebilir, bu yüzden TypeScript onların bir index signature'a uyduğunu varsaymaz; type alias'lar yeniden açılamaz, dolayısıyla bir type User = { name: string } geçerdi. Olağan çözümler şunlardır: bunun yerine object kabul edin (üzerinde yine Object.keys çağırabilirsiniz), fonksiyonu generic yapın (<T extends object>(obj: T)) ya da User tipini type ile bildirin (diğer farkları interface ve type sayfası ele alır).
Record, Index Signature ve Map
Record<K, V> | { [key: string]: V } | Map<K, V> | |
|---|---|---|---|
| Çalışma zamanında var mı | hayır, düz bir nesne | hayır, düz bir nesne | evet, bir class |
| Sabit anahtar kümesi | evet, union bir K ile | hayır | hayır |
| Anahtar tipleri | string, number, symbol, literal union'lar, template desenleri | string, number, symbol, template desenleri | her şey, nesneler dahil |
| Eksik anahtar okuma tipi | V (string anahtarlarla) | V | get ile V | undefined |
| Adlandırılmış özelliklerle karıştırma | intersection & ile | evet, aynı tipte | hayır |
| JSON ve spread | evet | evet | hayır, önce dönüştürün |
| Boyut | Object.keys(r).length | Object.keys(o).length | m.size |
| Sık ekleme ve silme | çalışır | çalışır | bunun için tasarlanmıştır |
Anahtar kümesi bilindiğinde union anahtarlı Record seçin: her anahtarın var olduğunu kontrol eden tek seçenek budur. Serbest string anahtarlar için Record<string, V> ve bir index signature birbirinin yerine kullanılabilir; birçok kod tabanı okunabilirlik için Record tercih eder. Anahtarlar çalışma zamanında eklenip siliniyorsa, anahtarlar string değilse ya da boyuta ve ekleme sırasına ek iş yapmadan ihtiyacınız varsa bir Map kullanın.
Sıkça Sorulan Sorular
TypeScript'te Record nedir?
Record<K, V>, anahtarları K tipinde ve değerlerinin hepsi V tipinde olan bir nesne için yerleşik bir utility type'tır. Record<string, number> herhangi bir string anahtara ve number değerlere sahip bir nesnedir; Record<"en" | "de", string> ise tam olarak en ve de anahtarlarına sahip, ikisi de string olan bir nesnedir.
TypeScript'te Record ile Map arasındaki fark nedir?
Record düz bir JavaScript nesnesi için bir tiptir; bu yüzden nesne literal'leri, JSON ve spread ile çalışır ve derleme zamanında kaybolur. Map ise get, set, has ve size içeren bir çalışma zamanı class'ıdır, tüm anahtarlar için ekleme sırasını korur, her anahtar tipini (nesneler dahil) kabul eder ve get metodu V | undefined döndürür. Sabit ya da JSON biçimli veri için Record, çalışma zamanında eklenip silinen anahtarlar için Map kullanın.
Record<string, T> ile { [key: string]: T } arasındaki fark nedir?
Değerler açısından aynı tiptir: Record<string, T>, string index signature'lı bir nesne tipine açılır. İki küçük fark vardır: bir index signature bir ad taşıyabilir ve aynı tipte başka özelliklerin yanında durabilir; ayrıca keyof Record<string, T> sonucu string iken keyof { [key: string]: T } sonucu string | number olur.
TypeScript'te bir Record üzerinde nasıl döngü kurarım?
Anahtar-değer çiftleri için Object.entries(record), anahtarlar için Object.keys, değerler için Object.values kullanın. Anahtarlar K olarak değil string olarak döner, çünkü bir nesne çalışma zamanında fazladan anahtarlar tutabilir. Record bilinen anahtarlardan oluşan bir union'a sahipse cast edin: (Object.keys(r) as Array<keyof typeof r>).
Bir Record'un yalnızca bazı anahtarlarını nasıl zorunlu yaparım?
Union anahtarlarla Record<K, V> her anahtarı zorunlu kılar. Hepsini isteğe bağlı yapmak için Partial ile sarın: Partial<Record<Lang, string>>. Karışık bir durum için kesiştirin: Record<"en", string> & Partial<Record<"de" | "fr", string>>, en anahtarını zorunlu kılar ve diğerlerine izin verir.