TypeScript mapped types, bir anahtar kümesi üzerinde dolaşarak yeni bir nesne tipi üreten generic tiplerdir. { [K in keyof T]: boolean } yazdığınızda T'nin her anahtarı için bir özellik oluşturulur ve değer tipi boolean olur. Partial, Readonly, Record gibi yerleşik tiplerin çoğu bu yöntemle yazılmıştır.
Bu yazıda mapped type sözdizimini, readonly ve ? değiştiricilerini + ve - ile eklemeyi ya da kaldırmayı, as ile anahtarları yeniden adlandırmayı ve süzmeyi TypeScript 6.0 ile denenmiş örneklerle anlatıyoruz. Kısa bir genel bakış için TypeScript ileri seviye özeti yazımıza da göz atabilirsiniz.
Index signature'dan mapped type'a

Mapped type sözdizimi index signature'a dayanır. Index signature, önceden bilinmeyen anahtarlar için tek bir değer tipi tanımlar:
type BayrakSozlugu = {
[anahtar: string]: boolean;
};
const b: BayrakSozlugu = { karanlikMod: true, beta: false };Mapped type ise in anahtar kelimesiyle belirli bir anahtar union'ı üzerinde dolaşır. Union çoğunlukla keyof ile elde edilir:
type SecenekBayraklari<T> = {
[Ozellik in keyof T]: boolean;
};
type Ozellikler = {
karanlikMod: () => void;
yeniProfil: () => void;
};
type OzellikSecenekleri = SecenekBayraklari<Ozellikler>;
// { karanlikMod: boolean; yeniProfil: boolean; }keyof operatörünün ayrıntıları için keyof ve indexed access yazımıza bakabilirsiniz. Değer tipinde T[Ozellik] yazarak orijinal tipi koruyabilir ya da dönüştürebilirsiniz.
Yerleşik tipleri kendiniz yazmak
Mapped type'ın nasıl çalıştığını görmenin en iyi yolu, yerleşik Partial ve Readonly'nin benzerlerini yazmaktır. TypeScript'in lib tanımlarındaki sürümler de aynı biçimdedir:
type Kismi<T> = { [K in keyof T]?: T[K] };
type SaltOkunur<T> = { readonly [K in keyof T]: T[K] };
type Sozluk<K extends keyof any, V> = { [P in K]: V };
interface Urun {
ad: string;
fiyat: number;
}
type U1 = Kismi<Urun>; // { ad?: string | undefined; fiyat?: number | undefined; }
type U2 = SaltOkunur<Urun>; // { readonly ad: string; readonly fiyat: number; }
type U3 = Sozluk<"tr" | "en", string>; // { tr: string; en: string; }Hazır sürümlerin tam listesi ve kullanım örnekleri utility types rehberimizde.
Değiştiriciler: +readonly, -readonly, +?, -?
Mapped type içinde iki değiştirici kullanılabilir: değiştirilebilirliği belirleyen readonly ve isteğe bağlılığı belirleyen ?. Önlerine - koyarak kaldırabilir, + koyarak ekleyebilirsiniz. Önek yazmazsanız + varsayılır. Bu kaldırma yeteneği TypeScript 2.8 ile geldi.
// readonly'yi kaldır
type Degistirilebilir<T> = {
-readonly [K in keyof T]: T[K];
};
type KilitliHesap = {
readonly id: string;
readonly ad: string;
};
type AcikHesap = Degistirilebilir<KilitliHesap>;
// { id: string; ad: string; }
// ? işaretini kaldır
type Kesin<T> = {
[K in keyof T]-?: T[K];
};
type BelkiKullanici = {
id: string;
ad?: string;
yas?: number;
};
type TamKullanici = Kesin<BelkiKullanici>;
// { id: string; ad: string; yas: number; }-? ile ? kaldırıldığında undefined da tipten çıkar; TamKullanici içinde ad artık string | undefined değil, string. TypeScript 2.8 sürüm notları bunu strictNullChecks açıkken geçerli bir davranış olarak tanımlar. 6.0 ile strict varsayılan olarak açık olduğu için yeni projelerde varsayılan davranış budur.
İki değiştiriciyi birlikte de kullanabilirsiniz:
type DegisirZorunlu<T> = { -readonly [K in keyof T]-?: T[K] };
type SaltOkunurKismi<T> = { +readonly [K in keyof T]+?: T[K] };
type Ham = { readonly a?: number };
type H1 = DegisirZorunlu<Ham>; // { a: number; }
type H2 = SaltOkunurKismi<{ b: string }>; // { readonly b?: string | undefined; }Homomorfik mapped types değiştiricileri korur
[K in keyof T] biçimindeki mapped type'lara homomorfik denir. TypeScript 2.8 sürüm notlarına göre bunlar varsayılan olarak orijinal tipin değiştiricilerini korur; - önekine ihtiyaç bu yüzden doğdu. Ayrıca TypeScript 3.1'den beri homomorfik bir mapped type bir tuple ya da dizi üzerinde çalıştırıldığında sonuç da tuple ya da dizi olur:
type Kopya<T> = { [K in keyof T]: T[K] };
type K1 = Kopya<{ readonly id: number; not?: string }>;
// { readonly id: number; not?: string | undefined; }
type PromiseYap<T> = { [K in keyof T]: Promise<T[K]> };
type Koordinat = [number, number];
type K2 = PromiseYap<Koordinat>; // [Promise<number>, Promise<number>]as ile anahtar yeniden adlandırma

TypeScript 4.1 ile gelen as cümleciği, her anahtarı başka bir anahtara dönüştürmenizi sağlar. Template literal types ile birleşince mevcut özellik adlarından yeni adlar üretebilirsiniz:
type Getterlar<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
interface Kisi {
ad: string;
yas: number;
sehir: string;
}
type TembelKisi = Getterlar<Kisi>;
// { getAd: () => string; getYas: () => number; getSehir: () => string; }string & K kesişimine dikkat edin. keyof T string dışında number ve symbol anahtarları da içerebilir; Capitalize ise yalnızca string kabul eder. Kesişim, anahtarı string olanlarla sınırlar.
Anahtarları süzmek: never üretmek
as cümleciği never üretirse o anahtar sonuçtan düşer. Bunu Exclude ya da kendi conditional type'ınızla kullanarak alanları süzebilirsiniz:
type TurAlaniniKaldir<T> = {
[K in keyof T as Exclude<K, "tur">]: T[K];
};
interface Daire {
tur: "daire";
yaricap: number;
}
type TursuzDaire = TurAlaniniKaldir<Daire>; // { yaricap: number; }
// Yalnızca string değerli alanları tut
type SadeceString<T> = {
[K in keyof T as T[K] extends string ? K : never]: T[K];
};
type S = SadeceString<{ ad: string; yas: number; eposta: string }>;
// { ad: string; eposta: string; }Herhangi bir union üzerinde dolaşmak
Mapped type yalnızca string | number | symbol union'ları üzerinde değil, nesne tiplerinden oluşan union'lar üzerinde de dolaşabilir. Anahtarı as ile her üyenin bir alanından üretirsiniz:
type OlayAyari<Olaylar extends { tur: string }> = {
[O in Olaylar as O["tur"]]: (olay: O) => void;
};
type KareOlayi = { tur: "kare"; x: number; y: number };
type DaireOlayi = { tur: "daire"; yaricap: number };
type Ayar = OlayAyari<KareOlayi | DaireOlayi>;
// { kare: (olay: KareOlayi) => void; daire: (olay: DaireOlayi) => void; }Conditional type ile değer dönüştürmek
Değer tipinde bir conditional type kullanarak her özelliği koşula göre farklı bir tipe çevirebilirsiniz. Handbook'taki örneğe benzer biçimde, kişisel veri (kisisel: true) işaretli alanları bulmak:
type KisiselVeriMi<T> = {
[K in keyof T]: T[K] extends { kisisel: true } ? true : false;
};
type DbAlanlari = {
id: { bicim: "artan" };
ad: { tip: string; kisisel: true };
};
type SilinecekAlanlar = KisiselVeriMi<DbAlanlari>;
// { id: false; ad: true; }Karşılaştırma tablosu
![Karşılaştırma tablosu: [K in keyof T]: T[K], [K in keyof T]?: T[K], [K in keyof T]-?: T[K], readonly [K in keyof T]: T[K]](/assets/blog/typescript-mapped-types-b6.webp)
| Yazım | Etkisi |
|---|---|
| [K in keyof T]: T[K] | Tipi aynen kopyalar, değiştiricileri korur |
| [K in keyof T]?: T[K] | Her özelliği isteğe bağlı yapar (Partial) |
| [K in keyof T]-?: T[K] | ? işaretini ve undefined'ı kaldırır (Required) |
| readonly [K in keyof T]: T[K] | Her özelliği readonly yapar (Readonly) |
| -readonly [K in keyof T]: T[K] | readonly'yi kaldırır |
| [K in keyof T as Yeni]: T[K] | Anahtarı yeniden adlandırır |
| [K in keyof T as ... never]: T[K] | never üreten anahtarları atar |
| [P in "a" | "b"]: V | Verilen anahtarlardan nesne kurar (Record) |
Sık yapılan hatalar
Capitalize'a doğrudan K vermek
type Hatali<T> = {
[K in keyof T as `get${Capitalize<K>}`]: () => T[K];
// TS2344: Type 'K' does not satisfy the constraint 'string'.
};Çözüm, yukarıdaki gibi string & K yazmaktır. Hata mesajının devamında derleyici, keyof T'nin number ve symbol de içerebildiğini ayrıca açıklar.
Mapped type'a ek özellik yazmak
Bir mapped type'ın süslü parantezi yalnızca eşleme ifadesini içerebilir. Yanına normal bir özellik eklemek hata verir; ek alanları bir intersection ile birleştirin:
type Yanlis<T> = {
[K in keyof T]: string;
ekstra: number;
};
// TS7061: A mapped type may not declare properties or methods.
type Dogru<T> = { [K in keyof T]: string } & { ekstra: number };Derinlik beklemek
Mapped type yalnızca bir seviyede çalışır. İç içe nesneleri de dönüştürmek istiyorsanız değer tipinde tipin kendisini özyinelemeli olarak çağırmanız gerekir. Bunu yaparken fonksiyon ve dizi gibi özel durumları ayrıca düşünmeniz gerekir.
Sık Sorulan Sorular
Mapped type ile index signature arasındaki fark nedir?
Index signature, bilinmeyen sayıda anahtar için tek bir değer tipi tanımlar. Mapped type ise belirli bir anahtar kümesi üzerinde dolaşır ve her anahtar için ayrı bir tip üretebilir. Ayrıntılı karşılaştırma için index signature ve Record yazımıza bakın.
as ile anahtar yeniden adlandırma hangi sürümde geldi?
Handbook'a göre TypeScript 4.1 ve sonrasında kullanılabilir. TypeScript 6.0 ile ek bir ayar gerekmez.
Mapped types interface ile yazılabilir mi?
Hayır. Mapped type sözdizimi yalnızca type alias'larında kullanılabilir. Interface ile type farkı için interface ve type karşılaştırmamıza bakabilirsiniz.
Readonly mapped type nesneyi gerçekten dondurur mu?
Hayır, yalnızca derleme zamanında atamayı engeller. Çalışma anında dondurma için Object.freeze gerekir; JavaScript nesneleri yazımızda bu konuya değiniyoruz.
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 indexed access · Conditional types · Template literal types · TypeScript ileri seviye özeti