TypeScript'te keyof bir nesne tipinin anahtarlarını string ya da sayı literal union'ı olarak verir, typeof (tip konumunda) bir değişkenin tipini yakalar, indexed access tipi T["alan"] ise bir tipin belirli bir özelliğinin tipini okur. Üçü birlikte kullanıldığında, bir sabit nesneden ya da diziden tipleri elle tekrar yazmadan türetebilirsiniz.

Bu yazıda üç operatörü ayrı ayrı ve birlikte, TypeScript 6.0 ile derlenmiş örneklerle anlatıyoruz. Bu operatörler generics, mapped types ve conditional types için temel yapı taşıdır; TypeScript ileri seviye özeti bu konuların kısa bir özetini verir.

keyof tip operatörü

keyof bir nesne tipini alır ve anahtarlarından oluşan bir literal union üretir:

typescript
type Nokta = { x: number; y: number };
type N = keyof Nokta; // "x" | "y"

interface Kullanici {
  id: number;
  ad: string;
  aktif: boolean;
}
type Alan = keyof Kullanici; // keyof Kullanici, yani "id" | "ad" | "aktif"

const a: Alan = "ad";     // geçerli
const b: Alan = "soyad";
// TS2322: Type '"soyad"' is not assignable to type 'keyof Kullanici'.

Tipin bir index signature'ı varsa keyof literal anahtarlar yerine o anahtar tipini döndürür. Burada çoğu kişiyi şaşırtan bir ayrıntı var: string index signature için sonuç string | number olur. Sebebi, JavaScript'te nesne anahtarlarının her zaman string'e dönüştürülmesidir; obj[0] ile obj["0"] aynı şeydir.

typescript
type Diziye = { [n: number]: unknown };
type A = keyof Diziye; // number

type Sozluk = { [k: string]: boolean };
type M = keyof Sozluk; // string | number

keyof asıl gücünü generic kısıtlarda gösterir. K extends keyof T kalıbıyla yalnızca var olan anahtarları kabul eden fonksiyonlar yazabilirsiniz; bu kalıbı generics yazımızda örnekledik.

typeof tip operatörü

typeof tip operatörü: typeof ile yapılandırma nesnesinden tip, Değer ile tip karışınca: TS2749, typeof'un sınırı

JavaScript'teki typeof bir ifade operatörüdür ve çalışma anında "string", "number" gibi bir metin döndürür. TypeScript, aynı anahtar kelimeyi tip konumunda da kullanmanıza izin verir; orada bir değişkenin ya da özelliğin derleme zamanındaki tipini verir. İki kullanım birbirinden tamamen farklıdır:

typescript
let s = "merhaba";
console.log(typeof s); // string  (JavaScript, çalışma anı)

let n: typeof s;       // string  (TypeScript, tip konumu)

const sabit = "merhaba";
type Sabit = typeof sabit; // "merhaba"

const ile tanımlanan bir string'in tipi literal "merhaba" olarak kalır, let ile tanımlanan genişler. Bu genişleme davranışı ve as const için literal tipler ve as const yazımıza bakabilirsiniz.

typeof ile yapılandırma nesnesinden tip

Pratikte typeof en çok, zaten yazılmış bir nesneden tip üretmek için kullanılır. Böylece tip ve değer tek yerde tanımlanır, birbirinden kopamaz:

typescript
const varsayilanAyar = {
  port: 3000,
  host: "localhost",
  gunluk: { seviye: "info", dosya: false },
};

type Ayar = typeof varsayilanAyar;
// { port: number; host: string; gunluk: { seviye: string; dosya: boolean; }; }

function baslat(ayar: Ayar) {
  console.log(`${ayar.host}:${ayar.port}`);
}

baslat(varsayilanAyar); // localhost:3000

Değer ile tip karışınca: TS2749

ReturnType gibi yerleşik tipler bir tip bekler. Bir fonksiyonun adını doğrudan verirseniz derleyici, değeri tip yerine kullandığınızı söyler ve typeof önerir:

typescript
function konum() {
  return { x: 10, y: 3 };
}

type P1 = ReturnType<konum>;
// TS2749: 'konum' refers to a value, but is being used as a type here. Did you mean 'typeof konum'?

type P2 = ReturnType<typeof konum>; // { x: number; y: number; }

typeof'un sınırı

TypeScript, typeof'u bilerek kısıtlar: yalnızca tanımlayıcılar (değişken adları) ve onların özellikleri üzerinde kullanılabilir. Bir fonksiyon çağrısının sonucunun tipini typeof f() ile alamazsınız; bunun amacı, çalıştığını sandığınız ama aslında çalışmayan kod yazmanızı önlemektir. Çağrı sonucunun tipi için ReturnType<typeof f> kullanın.

typescript
declare const onayla: (soru: string) => boolean;

let devam: typeof onayla("Emin misiniz?");
// TS1005: ',' expected.

let devam2: ReturnType<typeof onayla>; // boolean

Indexed access tipleri: T["alan"]

Indexed access tipi, köşeli parantez sözdizimiyle bir tipin belirli bir özelliğinin tipini okur. Parantezin içindeki şey de bir tiptir; bu yüzden union, keyof veya bir type alias kullanabilirsiniz:

typescript
type Kisi = { yas: number; ad: string; hayatta: boolean };

type Yas = Kisi["yas"];                // number
type I1 = Kisi["yas" | "ad"];          // string | number
type I2 = Kisi[keyof Kisi];            // string | number | boolean

type AdVeyaHayatta = "hayatta" | "ad";
type I3 = Kisi[AdVeyaHayatta];         // string | boolean

type Hatali = Kisi["yass"];
// TS2339: Property 'yass' does not exist on type 'Kisi'.

Kisi[keyof Kisi] kalıbı, bir nesnenin tüm değer tiplerinin union'ını verir ve sık kullanılır. Olmayan bir özelliği okumaya çalışırsanız derleme hatası alırsınız; bu da yazım hatalarını erkenden yakalar.

Dizi elemanının tipi: T[number]

Indeks olarak number kullanmak bir dizinin eleman tipini verir. typeof ile birleştirince bir dizi literalinden eleman tipini çıkarabilirsiniz:

typescript
const kullanicilar = [
  { ad: "Ali", yas: 15 },
  { ad: "Zeynep", yas: 23 },
];

type Kullanici = typeof kullanicilar[number]; // { ad: string; yas: number; }
type Yas = typeof kullanicilar[number]["yas"]; // number

Aynı teknik as const ile birleştiğinde, bir sabit listeden union tipi üretmenin en yaygın yoludur. Liste değişince tip de kendiliğinden güncellenir:

typescript
const ROLLER = ["yonetici", "editor", "okur"] as const;

type Rol = typeof ROLLER[number]; // "yonetici" | "editor" | "okur"

function yetkiVer(rol: Rol) {
  return ROLLER.indexOf(rol);
}

console.log(yetkiVer("editor")); // 1

Indeks olarak değer kullanılamaz

Köşeli parantezin içine yalnızca tip yazılabilir. Bir const değişkeni kullanırsanız iki hata birden alırsınız; çözüm, aynı değeri bir type alias olarak tanımlamaktır:

typescript
type Kisi = { yas: number; ad: string };

const anahtar = "yas";
type Y1 = Kisi[anahtar];
// TS2538: Type 'anahtar' cannot be used as an index type.
// TS2749: 'anahtar' refers to a value, but is being used as a type here. Did you mean 'typeof anahtar'?

type Anahtar = "yas";
type Y2 = Kisi[Anahtar];        // number
type Y3 = Kisi[typeof anahtar]; // number

Üçü birlikte: keyof typeof kalıbı

Gerçek projelerde en sık göreceğiniz birleşim keyof typeof nesne kalıbıdır. Önce typeof ile nesnenin tipini alır, sonra keyof ile anahtarlarını union'a çevirirsiniz. Indexed access de değer tipini verir:

typescript
const RENKLER = {
  kirmizi: "#ff0000",
  yesil: "#00ff00",
  mavi: "#0000ff",
} as const;

type Renk = keyof typeof RENKLER;          // "kirmizi" | "yesil" | "mavi"
type Hex = (typeof RENKLER)[Renk];         // "#ff0000" | "#00ff00" | "#0000ff"

function renkKodu(r: Renk): Hex {
  return RENKLER[r];
}

console.log(renkKodu("yesil")); // #00ff00

Karşılaştırma tablosu

Karşılaştırma tablosu: typeof x (JavaScript), typeof x (TypeScript), keyof T, T["alan"]
SözdizimiNeredeNe verirÖrnek sonuç
typeof x (JavaScript)İfade, çalışma anıTip adını string olarak"string"
typeof x (TypeScript)Tip konumux değişkeninin tipistring
keyof TTip konumuT anahtarlarının union'ı"x" | "y"
T["alan"]Tip konumuBir özelliğin tipinumber
T[keyof T]Tip konumuTüm değer tiplerinin union'ıstring | number
T[number]Tip konumuDizi eleman tipi{ ad: string }
keyof typeof objTip konumuBir değerin anahtar union'ı"kirmizi" | "yesil"

Sık yapılan hatalar

Object.keys sonucunu anahtar sanmak

Standart kütüphane tanımında Object.keys string[] döndürür, (keyof T)[] değil. Bu yüzden döngüde gelen anahtarla nesneyi indekslemek strict modda hata verir:

typescript
const fiyat = { cay: 20, kahve: 45 };

for (const k of Object.keys(fiyat)) {
  console.log(fiyat[k]);
  // TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ cay: number; kahve: number; }'.
  //   No index signature with a parameter of type 'string' was found on type '{ cay: number; kahve: number; }'.
}

Nesne yapısal tiplendiği için çalışma anında beklediğinizden fazla anahtar taşıyabilir; string[] tanımının sebebi budur. Anahtarların gerçekten sabit olduğundan eminseniz Object.entries ile değerlere doğrudan ulaşmak ya da anahtarı keyof typeof fiyat tipine bilinçli olarak dönüştürmek seçeneklerinizdir. Dönüşümün riskleri için type assertion yazımıza bakın.

keyof'u değere uygulamak

keyof yalnızca tiplere uygulanır. keyof RENKLER yazarsanız TS2749 hatası alırsınız; doğrusu keyof typeof RENKLER.

Genişlemiş tipleri beklemek

as const olmadan typeof ROLLER[number] size string verir, union değil. Literal tip istiyorsanız diziyi ya da nesneyi as const ile tanımlayın.

Sık Sorulan Sorular

keyof neden bazen string | number döndürüyor?

Tipin bir string index signature'ı varsa keyof sonucu string | number olur, çünkü JavaScript sayısal anahtarları da string'e çevirir. Bunu değiştiren eski seçenek TypeScript 6.0'da tamamen kaldırıldı.

typeof bir tipe uygulanabilir mi?

Hayır. Tip konumundaki typeof bir değerin tipini alır. Zaten bir tipiniz varsa doğrudan onu kullanırsınız.

T["a"]["b"] gibi zincirleme okuma yapabilir miyim?

Evet. Indexed access tipleri zincirlenebilir; typeof kullanicilar[number]["yas"] örneğinde olduğu gibi her adım bir önceki sonucun içine iner.

Bu operatörler JavaScript çıktısında görünür mü?

Tip konumundaki keyof, typeof ve indexed access tamamen derleme zamanına aittir ve çıktıdan silinir. Yalnızca ifade konumundaki typeof JavaScript'in kendi operatörüdür; onun davranışını JavaScript veri tipleri yazımızda bulabilirsiniz.

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 · Generics · Mapped types · Literal tipler ve as const · Utility types · TypeScript ileri seviye özeti