TypeScript Mapped Types: Dinamik Tip Dönüşümleri

TypeScript Mapped Types: Dinamik Tip Dönüşümleri
Mapped type'lar, mevcut tiplerin özelliklerini döngüleyerek yeni tipler oluşturmanızı sağlar. TypeScript'in en güçlü tip sistemi özelliklerinden biridir ve Partial, Required, Readonly gibi utility type'ların temelini oluşturur. Bu yazıda mapped type'ları sıfırdan ileri seviyeye kadar inceleyeceğiz.
Mapped Type Temelleri
// Temel mapped type syntax
type MappedType<T> = {
[K in keyof T]: T[K];
};
// Tüm özellikleri opsiyonel yapma (Partial'ın implementasyonu)
type Opsiyonel<T> = {
[K in keyof T]?: T[K];
};
// Tüm özellikleri readonly yapma
type SaltOkunur<T> = {
readonly [K in keyof T]: T[K];
};
// Tüm özellikleri zorunlu yapma
type Zorunlu<T> = {
[K in keyof T]-?: T[K]; // -? opsiyonelliği kaldırır
};
// Readonly'yi kaldırma
type Yazilabilir<T> = {
-readonly [K in keyof T]: T[K];
};
// Kullanım
interface Kullanici {
readonly id: number;
isim: string;
email?: string;
}
type OpsiyonelKullanici = Opsiyonel<Kullanici>;
type ZorunluKullanici = Zorunlu<Kullanici>;
type YazilabilirKullanici = Yazilabilir<Kullanici>;
Key Remapping (as ile)
TypeScript 4.1 ile gelen key remapping, anahtar adlarını dönüştürmenizi sağlar:
// Getter tipi oluşturma
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
interface Kisi {
isim: string;
yas: number;
}
type KisiGetters = Getters<Kisi>;
// { getIsim: () => string; getYas: () => number; }
// Belirli tipleri filtreleme
type SadeceFonksiyonlar<T> = {
[K in keyof T as T[K] extends Function ? K : never]: T[K];
};
interface Karisik {
isim: string;
yas: number;
selamla(): void;
hesapla(x: number): number;
}
type SadeceMetotlar = SadeceFonksiyonlar<Karisik>;
// { selamla: () => void; hesapla: (x: number) => number; }
// Event handler tipleri
type EventHandlers<T> = {
[K in keyof T as `on${Capitalize<string & K>}Change`]: (
yeniDeger: T[K],
eskiDeger: T[K]
) => void;
};
type KullaniciEventHandlers = EventHandlers<Kullanici>;
// {
// onIdChange: (yeniDeger: number, eskiDeger: number) => void;
// onIsimChange: (yeniDeger: string, eskiDeger: string) => void;
// onEmailChange: ...
// }
never döndürmek, o anahtarı sonuç tipinden tamamen çıkarır. Bu, filtreleme işlemleri için çok kullanışlıdır.
Mapped Type ile Utility Type İmplementasyonları
// Pick implementasyonu
type MyPick<T, K extends keyof T> = {
[P in K]: T[P];
};
// Omit implementasyonu
type MyOmit<T, K extends keyof T> = {
[P in keyof T as P extends K ? never : P]: T[P];
};
// Record implementasyonu
type MyRecord<K extends keyof any, T> = {
[P in K]: T;
};
// Readonly deep implementasyonu
type DeepReadonly<T> = {
readonly [K in keyof T]: T[K] extends object
? T[K] extends Function
? T[K]
: DeepReadonly<T[K]>
: T[K];
};
// Partial deep implementasyonu
type DeepPartial<T> = {
[K in keyof T]?: T[K] extends object
? T[K] extends Function
? T[K]
: DeepPartial<T[K]>
: T[K];
};
// Kullanım
interface Ayarlar {
genel: {
tema: string;
dil: string;
bildirimler: {
email: boolean;
push: boolean;
};
};
profil: {
isim: string;
avatar: string;
};
}
type KismiAyarlar = DeepPartial<Ayarlar>;
// Tüm iç içe özellikler de opsiyonel olur
Pratik Mapped Type Örnekleri
// Form doğrulama tipleri
type FormHatalari<T> = {
[K in keyof T]?: string[];
};
type FormDokunuldu<T> = {
[K in keyof T]: boolean;
};
interface KayitFormu {
isim: string;
email: string;
sifre: string;
}
const hatalar: FormHatalari<KayitFormu> = {
email: ["Geçerli bir email adresi girin"],
sifre: ["En az 8 karakter olmalı", "Büyük harf içermeli"]
};
const dokunuldu: FormDokunuldu<KayitFormu> = {
isim: true,
email: true,
sifre: false
};
// API endpoint tipleri
type CrudEndpoints<T extends Record<string, any>> = {
[K in keyof T as `${string & K}Getir`]: (id: number) => Promise<T[K]>;
} & {
[K in keyof T as `${string & K}Listele`]: () => Promise<T[K][]>;
} & {
[K in keyof T as `${string & K}Olustur`]: (veri: Omit<T[K], "id">) => Promise<T[K]>;
} & {
[K in keyof T as `${string & K}Sil`]: (id: number) => Promise<boolean>;
};
interface Modeller {
kullanici: { id: number; isim: string };
urun: { id: number; ad: string; fiyat: number };
}
type Api = CrudEndpoints<Modeller>;
// kullaniciGetir, kullaniciListele, kullaniciOlustur, kullaniciSil
// urunGetir, urunListele, urunOlustur, urunSil
Sonuç
Mapped type'lar, TypeScript tip sisteminin en güçlü araçlarından biridir. Mevcut tiplerin özelliklerini dönüştürme, filtreleme ve yeniden adlandırma yeteneği ile kod tekrarını minimize edebilir ve tip güvenliğini artırabilirsiniz. Utility type'ların iç yapısını anlamak, kendi özel mapped type'larınızı oluşturmanızda size rehberlik edecektir.