Index signature, bir nesnenin anahtarlarını önceden bilmediğiniz ama değerlerinin tipini bildiğiniz durumları tanımlar: { [anahtar: string]: number } "her metin anahtar bir sayı tutar" demektir. Record<Keys, Type> aynı fikrin yardımcı tip hâlidir ve anahtarları bir birleşim tipiyle sınırlamak istediğinizde öne çıkar. noUncheckedIndexedAccess ise index signature üzerinden okunan her değere undefined ekleyerek "bu anahtar gerçekten var mı?" sorusunu size sordurur.

Bu yazıda üçünü birlikte, TypeScript 6.0 derleyicisiyle denenmiş örneklerle ele alıyoruz. Dikkat: noUncheckedIndexedAccess strict ailesinin parçası değildir; TypeScript 6.0 ile strict varsayılan olarak açık gelse de bu seçeneği ayrıca açmanız gerekir.

Index signature sözdizimi

Index signature sözdizimi: Number ve string anahtarlar birlikte, readonly ve template string anahtarlar

Köşeli parantez içine bir anahtar adı ve tipi, iki noktadan sonra da değer tipini yazarsınız. Anahtar adı yalnızca okunabilirlik içindir. Handbook'a göre anahtar tipi string, number, symbol, template string kalıpları ya da yalnızca bunlardan oluşan birleşimler olabilir.

typescript
interface Fiyatlar {
  [urunKodu: string]: number;
}

const fiyat: Fiyatlar = { kalem: 25, defter: 60 };
fiyat["silgi"] = 10;
const k = fiyat["kalem"]; // number
console.log(k, Object.keys(fiyat).length); // 25 3

Bu tanım fiyat.kalem ile fiyat["kalem"] erişimlerini eşdeğer sayar. Bu yüzden string index signature, aynı arayüzdeki adlandırılmış tüm özelliklerin de aynı değer tipine uymasını zorunlu kılar:

typescript
interface Ayarlar {
  [anahtar: string]: number;
  zamanAsimi: number;
  ad: string;
  // TS2411: Property 'ad' of type 'string' is not assignable to 'string' index type 'number'.
}

Çözüm, index signature değerini özelliklerin tiplerini kapsayan bir birleşime genişletmektir: [anahtar: string]: number | string. Bu durumda zamanAsimi ve ad kabul edilir.

Number ve string anahtarlar birlikte

JavaScript sayısal anahtarı nesneye erişmeden önce metne çevirir; obj[100] ile obj["100"] aynı şeydir. Bu nedenle bir tipte hem number hem string index signature varsa, number olanın değer tipi string olanın alt tipi olmalıdır. Anahtar tipleri ve dönüşümler hakkında daha fazlası için JavaScript Object metotları yazısına bakabilirsiniz.

readonly ve template string anahtarlar

typescript
interface SabitListe {
  readonly [sira: number]: string;
}
const renkler: SabitListe = ["kirmizi", "mavi"];
renkler[0] = "yesil";
// TS2542: Index signature in type 'SabitListe' only permits reading.

interface VeriOzellikleri {
  [ad: `data-${string}`]: string;
}
const o: VeriOzellikleri = { "data-id": "42", "data-rol": "menu" };
const hatali: VeriOzellikleri = { "aria-label": "x" };
// TS2353: Object literal may only specify known properties, and '"aria-label"'
// does not exist in type 'VeriOzellikleri'.

Template string kalıbıyla anahtar tanımlama TypeScript 4.4 ile geldi. Template literal tiplerin kendisi için template literal types yazısına bakın.

Record&lt;Keys, Type&gt;

Record<Keys, Type>, anahtarları Keys, değerleri Type olan bir nesne tipi oluşturur. Record<string, number> pratikte { [k: string]: number } ile aynı işi görür. Asıl farkı, Keys olarak bir literal birleşim verdiğinizde ortaya çıkar: her anahtar zorunlu hâle gelir ve eksik anahtar derleme hatasıdır.

typescript
type Ortam = "gelistirme" | "test" | "canli";

const adresler: Record<Ortam, string> = {
  gelistirme: "http://localhost:3000",
  test: "https://test.ornek.com",
};
// TS2741: Property 'canli' is missing in type '{ gelistirme: string; test: string; }'
// but required in type 'Record<Ortam, string>'.

Üç anahtarı da yazdığınızda kod derlenir ve adresler[o] okuması undefined ihtimali olmadan string döndürür, çünkü anahtar kümesi sonludur. Sayaç gibi anahtarların çalışma zamanında oluştuğu durumlarda ise Record<string, number> kullanılır:

typescript
const sayac: Record<string, number> = {};
for (const kelime of ["a", "b", "a"]) {
  sayac[kelime] = (sayac[kelime] ?? 0) + 1;
}
console.log(sayac); // { a: 2, b: 1 }
YazımAnahtar kümesiEksik anahtarTipik kullanım
{ [k: string]: T }SınırsızHata değilSözlük, sayaç, dinamik veri
Record<string, T>SınırsızHata değilAynı; daha kısa yazım
Record<"a" | "b", T>Sonlu birleşimTS2741 hatasıHer duruma bir değer eşleme
Map<K, V>Sınırsızget undefined dönerSık ekleme ve silme, nesne olmayan anahtarlar

Record ve diğer yardımcı tipler utility types yazısında, anahtar birleşimini keyof ile türetmek ise keyof, typeof ve indexed access yazısında anlatılıyor. Mapped type ile kendi Record benzeri tiplerinizi kurmak için mapped types yazısına bakın.

Partial Record ve generic sözlükler

Anahtar kümesi belli olduğu hâlde her anahtar için değer zorunlu değilse Partial<Record<Ortam, number>> kullanılabilir. Bu durumda yazım hataları yine yakalanır, ama okunan değerin tipi number | undefined olur ve kontrol etmeniz gerekir. Dinamik anahtar üreten yardımcı fonksiyonlarda ise dönüş tipini Record<string, T[]> yazmak niyeti açıkça belirtir:

typescript
type Ortam = "gelistirme" | "test" | "canli";

const zamanAsimi: Partial<Record<Ortam, number>> = { canli: 5000 };
const t = zamanAsimi.test; // number | undefined
console.log(t ?? 1000);    // 1000

function grupla<T>(liste: T[], anahtar: (x: T) => string): Record<string, T[]> {
  const sonuc: Record<string, T[]> = {};
  for (const x of liste) {
    const k = anahtar(x);
    (sonuc[k] ??= []).push(x);
  }
  return sonuc;
}
console.log(grupla(["elma", "armut", "erik"], (m) => m[0]));
// { e: [ 'elma', 'erik' ], a: [ 'armut' ] }

??= operatörü yalnızca sol taraf null veya undefined ise atama yapar; burada henüz oluşmamış grubu boş diziyle başlatmak için kullanıldı. Benzer bir gruplama için yerleşik Object.groupBy da vardır; TypeScript bu metodun tiplerini 5.4 sürümünde ekledi. Generic fonksiyon yazımının ayrıntıları generics yazısında.

noUncheckedIndexedAccess: var olmayan anahtar sorunu

Varsayılan ayarda index signature üzerinden okunan değer her zaman var kabul edilir. Oysa çoğu nesne olası her anahtar için değer tutmaz. Aşağıdaki kod strict modda bile hatasız derlenir, ama çalışma zamanında çöker:

typescript
const stok: Record<string, number> = { elma: 3 };
const armut = stok["armut"];   // tip: number
console.log(armut.toFixed(0));
// Node.js: TypeError: Cannot read properties of undefined (reading 'toFixed')

const dizi = [10, 20, 30];
const dorduncu = dizi[3];      // tip: number
console.log(dorduncu.toFixed(0));

TypeScript 4.1 ile gelen noUncheckedIndexedAccess açıldığında, bildirilmemiş bir özelliğe veya dizi indeksine yapılan her erişim T | undefined tipinde olur. Aynı dosya bu kez derlenmez:

typescript
// tsc --noEmit --noUncheckedIndexedAccess stok.ts
// TS18048: 'armut' is possibly 'undefined'.
// TS18048: 'dorduncu' is possibly 'undefined'.

Hatayı gidermenin yolu değeri kullanmadan önce kontrol etmek ya da varsayılan değer vermektir. for...of döngüsü dizinin gerçek öğelerini gezdiği için orada undefined eklenmez:

typescript
const stok: Record<string, number> = { elma: 3 };

const armut = stok["armut"]; // number | undefined
if (armut !== undefined) {
  console.log(armut.toFixed(0));
}
console.log(stok["armut"] ?? 0); // 0

for (const deger of [10, 20, 30]) {
  console.log(deger.toFixed(0)); // deger: number
}
json
{
  "compilerOptions": {
    "noUncheckedIndexedAccess": true
  }
}

noPropertyAccessFromIndexSignature

Bu seçenek, index signature'dan gelen anahtarlara nokta ile erişmeyi yasaklar ve köşeli parantez ister. Amaç, kod okurken bir özelliğin tipte bildirilmiş mi yoksa tahmin mi edildiğini erişim biçiminden anlayabilmektir:

typescript
interface OyunAyari {
  hiz: "hizli" | "yavas";
  [anahtar: string]: string;
}
declare const ayar: OyunAyari;
ayar.hiz;            // tamam, bildirilmiş özellik
ayar["kullanici"];   // tamam
ayar.kullanici;
// TS4111: Property 'kullanici' comes from an index signature, so it must be
// accessed with ['kullanici'].

Sık yapılan hatalar

Sık yapılan hatalar: Bilinen anahtarları olan bir nesneye Record<string, T> vermek. Anahtarlar..., String index signature ile farklı tipte adlandırılmış özellikleri aynı...
  • Bilinen anahtarları olan bir nesneye Record<string, T> vermek. Anahtarlar belliyse literal birleşim kullanın; yazım hataları derleme anında yakalanır.
  • String index signature ile farklı tipte adlandırılmış özellikleri aynı arayüzde tutmaya çalışmak (TS2411). Değer tipini birleşim yapın veya dinamik kısmı ayrı bir özelliğe taşıyın.
  • noUncheckedIndexedAccess açıkken her erişime ! eklemek. Bu, seçeneğin sağladığı güvenliği geri almak demektir.
  • Sık ekleme ve silme yapılan, anahtarı nesne olan koleksiyonlar için düz nesne kullanmak. Bu durumda Map daha uygundur; ayrıntılar Map, Set ve WeakMap yazısında.

Sık Sorulan Sorular

Index signature ile Record arasında performans farkı var mı?

Hayır. İkisi de yalnızca tip düzeyindedir ve derleme çıktısında iz bırakmaz; çalışma zamanında elinizde sıradan bir JavaScript nesnesi vardır. Seçim okunabilirlik ve anahtar kümesinin sonlu olup olmamasına göre yapılır.

noUncheckedIndexedAccess strict ile açılıyor mu?

Hayır. Bu seçenek strict ailesinde değildir ve TypeScript 6.0'da da varsayılan olarak kapalıdır. tsconfig.json içinde ayrıca açmanız gerekir.

Tuple elemanlarına erişimde de undefined eklenir mi?

Tuple tiplerinde uzunluk bilindiği için bildirilmiş indekslerin tipi değişmez; sınır dışı indeks zaten TS2493 hatası verir. Ayrıntılar tuple ve readonly diziler yazısında.

Symbol anahtarlı index signature yazılabilir mi?

Evet. Handbook'un izin verdiği anahtar tiplerinden biri symbol'dür; TypeScript 4.4 ile symbol ve template string kalıplı index signature desteği geldi:

typescript
const sembol = Symbol("id");
interface Etiketler {
  [anahtar: symbol]: string;
}
const e: Etiketler = { [sembol]: "birinci" };
console.log(e[sembol]); // birinci

Symbol'lerin çalışma zamanındaki davranışı için JavaScript Symbol yazısına bakabilirsiniz.

Anahtar olarak enum kullanabilir miyim?

Record ile evet: Record<Durum, string> enum üyelerinin hepsini zorunlu kılar. Enum yerine literal birleşim kullanma seçenekleri için enum ve alternatifleri yazısına bakın.

Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: TypeScript öğrenme rehberi · Utility types · keyof ve typeof · Mapped types · strictNullChecks · Tuple tipleri · TypeScript ileri seviye