Metadata programlama, aspect-oriented programlama ve bildirimsel desenlerle kodu geliştirmek için TypeScript Decorator'larının gücünü keşfedin. Global geliştiriciler için kapsamlı bir rehber.
TypeScript Decorator'ları: Sağlam Uygulamalar için Metadata Programlama Desenlerinde Uzmanlaşma
Modern yazılım geliştirmenin geniş dünyasında, temiz, ölçeklenebilir ve yönetilebilir kod tabanlarını korumak esastır. TypeScript, güçlü tip sistemi ve gelişmiş özellikleriyle, geliştiricilere bunu başarmaları için araçlar sunar. En ilgi çekici ve dönüştürücü özelliklerinden biri de Decorator'lardır. Bu yazının yazıldığı sırada hala deneysel bir özellik (ECMAScript için Aşama 3 teklifi) olmalarına rağmen, decorator'lar Angular ve TypeORM gibi framework'lerde yaygın olarak kullanılmakta ve tasarım desenlerine, metadata programlamaya ve aspect-oriented programlamaya (AOP) yaklaşımımızı temelden değiştirmektedir.
Bu kapsamlı rehber, TypeScript decorator'larını derinlemesine inceleyecek, mekaniklerini, çeşitli türlerini, pratik uygulamalarını ve en iyi uygulamaları keşfedecektir. İster büyük ölçekli kurumsal uygulamalar, ister mikro servisler veya istemci tarafı web arayüzleri oluşturuyor olun, decorator'ları anlamak daha bildirimsel, sürdürülebilir ve güçlü TypeScript kodu yazmanızı sağlayacaktır.
Temel Kavramı Anlamak: Decorator Nedir?
Özünde bir decorator, bir sınıf bildirimine, metoda, erişimciye, özelliğe veya parametreye eklenebilen özel bir bildirim türüdür. Decorator'lar, dekore ettikleri hedef için yeni bir değer döndüren (veya mevcut olanı değiştiren) fonksiyonlardır. Birincil amaçları, altta yatan kod yapısını doğrudan değiştirmeden, eklendikleri bildirimin davranışını değiştirmek veya metadata eklemektir. Kodu artırmanın bu harici, bildirimsel yolu inanılmaz derecede güçlüdür.
Decorator'ları, kodunuzun bölümlerine uyguladığınız ek açıklamalar veya etiketler olarak düşünün. Bu etiketler daha sonra uygulamanızın diğer bölümleri veya framework'ler tarafından, genellikle çalışma zamanında, ek işlevsellik veya yapılandırma sağlamak için okunabilir veya üzerinde işlem yapılabilir.
Decorator Sözdizimi
Decorator'lar, bir @
sembolü ile başlar ve ardından decorator fonksiyonunun adı gelir. Dekore ettikleri bildirimin hemen önüne yerleştirilirler.
@MyDecorator\nclass MyClass {\n @AnotherDecorator\n myMethod() {\n // ...\n }\n}
TypeScript'te Decorator'ları Etkinleştirme
Decorator'ları kullanmadan önce, tsconfig.json
dosyanızda experimentalDecorators
derleyici seçeneğini etkinleştirmeniz gerekir. Ek olarak, gelişmiş metadata yansıtma yetenekleri için (genellikle framework'ler tarafından kullanılır), emitDecoratorMetadata
ve reflect-metadata
polyfill'ine de ihtiyacınız olacaktır.
// tsconfig.json\n{\n "compilerOptions": {\n "target": "ES2017",\n "module": "commonjs",\n "experimentalDecorators": true,\n "emitDecoratorMetadata": true,\n "outDir": "./dist",\n "strict": true,\n "esModuleInterop": true,\n "skipLibCheck": true,\n "forceConsistentCasingInFileNames": true\n }\n}
Ayrıca reflect-metadata
'yı yüklemeniz gerekir:
npm install reflect-metadata --save\n# veya\nyarn add reflect-metadata
Ve bunu uygulamanızın giriş noktasının (ör. main.ts
veya app.ts
) en başına import etmeniz gerekir:
import "reflect-metadata";\n// Uygulama kodunuz burada devam eder
Decorator Fabrikaları: Parmaklarınızın Ucundaki Özelleştirme
Basit bir decorator bir fonksiyon olsa da, davranışını yapılandırmak için genellikle bir decorator'a argümanlar geçirmeniz gerekir. Bu, bir decorator fabrikası kullanılarak gerçekleştirilir. Bir decorator fabrikası, asıl decorator fonksiyonunu döndüren bir fonksiyondur. Bir decorator fabrikası uyguladığınızda, onu argümanlarıyla çağırırsınız ve o da TypeScript'in kodunuza uyguladığı decorator fonksiyonunu döndürür.
Basit Bir Decorator Fabrikası Örneği Oluşturma
Farklı öneklerle mesajları loglayabilen bir Logger
decorator'ı için bir fabrika oluşturalım.
function Logger(prefix: string) {\n return function (target: Function) {\n console.log(`[${prefix}] ${target.name} sınıfı tanımlandı.`);\n };\n}\n\n@Logger("APP_INIT")\nclass ApplicationBootstrap {\n constructor() {\n console.log("Uygulama başlatılıyor...");\n }\n}\n\nconst app = new ApplicationBootstrap();\n// Çıktı:\n// [APP_INIT] ApplicationBootstrap sınıfı tanımlandı.\n// Uygulama başlatılıyor...
Bu örnekte, Logger("APP_INIT")
decorator fabrikası çağrısıdır. Argüman olarak target: Function
(sınıf yapıcısı) alan asıl decorator fonksiyonunu döndürür. Bu, decorator davranışının dinamik olarak yapılandırılmasına olanak tanır.
TypeScript'teki Decorator Türleri
TypeScript, her biri belirli bir bildirim türüne uygulanabilen beş farklı decorator türünü destekler. Decorator fonksiyonunun imzası, uygulandığı bağlama göre değişir.
1. Sınıf Decorator'ları
Sınıf decorator'ları, sınıf bildirimlerine uygulanır. Decorator fonksiyonu, tek argümanı olarak sınıfın yapıcısını (constructor) alır. Bir sınıf decorator'ı bir sınıf tanımını gözlemleyebilir, değiştirebilir veya hatta değiştirebilir.
İmza:
function ClassDecorator(target: Function) { ... }
Dönüş Değeri:
Eğer sınıf decorator'ı bir değer döndürürse, sınıf bildirimini sağlanan yapılandırıcı fonksiyonuyla değiştirir. Bu, genellikle mixin'ler veya sınıf genişletme için kullanılan güçlü bir özelliktir. Eğer bir değer döndürülmezse, orijinal sınıf kullanılır.
Kullanım Alanları:
- Sınıfları bir bağımlılık enjeksiyonu (dependency injection) kabına kaydetme.
- Bir sınıfa mixin'ler veya ek işlevler uygulama.
- Framework'e özgü yapılandırmalar (ör. bir web framework'ünde yönlendirme).
- Sınıflara yaşam döngüsü kancaları (lifecycle hooks) ekleme.
Sınıf Decorator'ı Örneği: Bir Servis Enjekte Etme
Bir sınıfı "enjekte edilebilir" olarak işaretlemek ve isteğe bağlı olarak bir kap (container) içinde ona bir isim vermek istediğiniz basit bir bağımlılık enjeksiyonu senaryosu hayal edin.
const InjectableServiceRegistry = new Map<string, Function>();\n\nfunction Injectable(name?: string) {\n return function<T extends { new(...args: any[]): {} }>(constructor: T) {\n const serviceName = name || constructor.name;\n InjectableServiceRegistry.set(serviceName, constructor);\n console.log(`Kayıtlı servis: ${serviceName}`);\n\n // İsteğe bağlı olarak, davranışı artırmak için burada yeni bir sınıf döndürebilirsiniz\n return class extends constructor {\n createdAt = new Date();\n // Tüm enjekte edilen servisler için ek özellikler veya metotlar\n };\n };\n}\n\n@Injectable("UserService")\nclass UserDataService {\n getUsers() {\n return [{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }];\n }\n}\n\n@Injectable()\nclass ProductDataService {\n getProducts() {\n return [{ id: 101, name: "Laptop" }, { id: 102, name: "Mouse" }];\n }\n}\n\nconsole.log("--- Servisler Kaydedildi ---");\nconsole.log(Array.from(InjectableServiceRegistry.keys()));\n\nconst userServiceConstructor = InjectableServiceRegistry.get("UserService");\nif (userServiceConstructor) {\n const userServiceInstance = new userServiceConstructor();\n console.log("Kullanıcılar:", userServiceInstance.getUsers());\n // console.log("Kullanıcı Servisi Oluşturulma Zamanı:", userServiceInstance.createdAt); // Eğer döndürülen sınıf kullanılırsa\n}
Bu örnek, bir sınıf decorator'ının bir sınıfı nasıl kaydedebileceğini ve hatta yapıcısını nasıl değiştirebileceğini göstermektedir. Injectable
decorator'ı, sınıfın teorik bir bağımlılık enjeksiyonu sistemi tarafından keşfedilebilir olmasını sağlar.
2. Metot Decorator'ları
Metot decorator'ları, metot bildirimlerine uygulanır. Üç argüman alırlar: hedef nesne (statik üyeler için yapılandırıcı fonksiyon; örnek üyeler için sınıfın prototipi), metodun adı ve metodun özellik tanımlayıcısı (property descriptor).
İmza:
function MethodDecorator(target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) { ... }
Dönüş Değeri:
Bir metot decorator'ı yeni bir PropertyDescriptor
döndürebilir. Eğer döndürürse, bu tanımlayıcı metodu tanımlamak için kullanılır. Bu, orijinal metodun uygulamasını değiştirmenize veya yenisiyle değiştirmenize olanak tanır, bu da onu AOP için inanılmaz derecede güçlü kılar.
Kullanım Alanları:
- Metot çağrılarını ve argümanlarını/sonuçlarını loglama.
- Performansı artırmak için metot sonuçlarını önbelleğe alma.
- Metot yürütülmeden önce yetkilendirme kontrolleri uygulama.
- Metot yürütme süresini ölçme.
- Metot çağrılarını debouncing veya throttling yapma.
Metot Decorator'ı Örneği: Performans İzleme
Bir metodun yürütme süresini loglamak için bir MeasurePerformance
decorator'ı oluşturalım.
function MeasurePerformance(target: Object, propertyKey: string, descriptor: PropertyDescriptor) {\n const originalMethod = descriptor.value;\n\n descriptor.value = function(...args: any[]) {\n const start = process.hrtime.bigint();\n const result = originalMethod.apply(this, args);\n const end = process.hrtime.bigint();\n const duration = Number(end - start) / 1_000_000;\n console.log(`"${propertyKey}" metodu ${duration.toFixed(2)} ms içinde yürütüldü`);\n return result;\n };\n\n return descriptor;\n}\n\nclass DataProcessor {\n @MeasurePerformance\n processData(data: number[]): number[] {\n // Karmaşık, zaman alıcı bir işlemi simüle et\n for (let i = 0; i < 1_000_000; i++) {\n Math.sin(i);\n }\n return data.map(n => n * 2);\n }\n\n @MeasurePerformance\n fetchRemoteData(id: string): Promise<string> {\n return new Promise(resolve => {\n setTimeout(() => {\n resolve(`ID için veri: ${id}`);\n }, 500);\n });\n }\n}\n\nconst processor = new DataProcessor();\nprocessor.processData([1, 2, 3]);\nprocessor.fetchRemoteData("abc").then(result => console.log(result));
MeasurePerformance
decorator'ı, orijinal metodu zamanlama mantığıyla sarmalar ve metodun içindeki iş mantığını karıştırmadan yürütme süresini yazdırır. Bu, Aspect-Oriented Programlama'nın (AOP) klasik bir örneğidir.
3. Erişimci Decorator'ları
Erişimci decorator'ları, erişimci (get
ve set
) bildirimlerine uygulanır. Metot decorator'larına benzer şekilde, hedef nesneyi, erişimcinin adını ve özellik tanımlayıcısını alırlar.
İmza:
function AccessorDecorator(target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) { ... }
Dönüş Değeri:
Bir erişimci decorator'ı, erişimciyi tanımlamak için kullanılacak olan yeni bir PropertyDescriptor
döndürebilir.
Kullanım Alanları:
- Bir özellik ayarlanırken doğrulama yapma.
- Bir değer ayarlanmadan önce veya alındıktan sonra dönüştürme.
- Özellikler için erişim izinlerini kontrol etme.
Erişimci Decorator'ı Örneği: Getter'ları Önbelleğe Alma
Maliyetli bir getter hesaplamasının sonucunu önbelleğe alan bir decorator oluşturalım.
function CachedGetter(target: any, propertyKey: string, descriptor: PropertyDescriptor) {\n const originalGetter = descriptor.get;\n const cacheKey = `_cached_${String(propertyKey)}`;\n\n if (originalGetter) {\n descriptor.get = function() {\n if (this[cacheKey] === undefined) {\n console.log(`[Cache Miss] ${String(propertyKey)} için değer hesaplanıyor`);\n this[cacheKey] = originalGetter.apply(this);\n } else {\n console.log(`[Cache Hit] ${String(propertyKey)} için önbellekteki değer kullanılıyor`);\n }\n return this[cacheKey];\n };\n }\n return descriptor;\n}\n\nclass ReportGenerator {\n private data: number[];\n\n constructor(data: number[]) {\n this.data = data;\n }\n\n // Maliyetli bir hesaplamayı simüle eder\n @CachedGetter\n get expensiveSummary(): number {\n console.log("Maliyetli özet hesaplaması yapılıyor...");\n return this.data.reduce((sum, current) => sum + current, 0) / this.data.length;\n }\n}\n\nconst generator = new ReportGenerator([10, 20, 30, 40, 50]);\n\nconsole.log("İlk erişim:", generator.expensiveSummary);\nconsole.log("İkinci erişim:", generator.expensiveSummary);\nconsole.log("Üçüncü erişim:", generator.expensiveSummary);
Bu decorator, expensiveSummary
getter'ının hesaplamasının yalnızca bir kez çalışmasını sağlar, sonraki çağrılar önbellekteki değeri döndürür. Bu desen, özellik erişiminin ağır hesaplama veya harici çağrılar içerdiği durumlarda performansı optimize etmek için çok kullanışlıdır.
4. Özellik Decorator'ları
Özellik decorator'ları, özellik bildirimlerine uygulanır. İki argüman alırlar: hedef nesne (statik üyeler için yapılandırıcı fonksiyon; örnek üyeler için sınıfın prototipi) ve özelliğin adı.
İmza:
function PropertyDecorator(target: Object, propertyKey: string | symbol) { ... }
Dönüş Değeri:
Özellik decorator'ları herhangi bir değer döndüremez. Birincil kullanımları, özellik hakkında metadata kaydetmektir. Dekorasyon sırasında özelliğin değerini veya tanımlayıcısını doğrudan değiştiremezler, çünkü özellik decorator'ları çalıştığında bir özelliğin tanımlayıcısı henüz tam olarak tanımlanmamıştır.
Kullanım Alanları:
- Serileştirme/deserileştirme için özellikleri kaydetme.
- Özelliklere doğrulama kuralları uygulama.
- Özellikler için varsayılan değerler veya yapılandırmalar ayarlama.
- ORM (Object-Relational Mapping) sütun eşlemesi (ör. TypeORM'deki
@Column()
).
Özellik Decorator'ı Örneği: Gerekli Alan Doğrulaması
Bir özelliği "gerekli" olarak işaretlemek ve ardından çalışma zamanında doğrulamak için bir decorator oluşturalım.
interface ValidationRule {\n property: string | symbol;\n validate: (value: any) => boolean;\n message: string;\n}\n\nconst validationRules: Map<Function, ValidationRule[]> = new Map();\n\nfunction Required(target: Object, propertyKey: string | symbol) {\n const rules = validationRules.get(target.constructor) || [];\n rules.push({\n property: propertyKey,\n validate: (value: any) => value !== null && value !== undefined && value !== "",\n message: `${String(propertyKey)} gereklidir.`\n });\n validationRules.set(target.constructor, rules);\n}\n\nfunction validate(instance: any): string[] {\n const classRules = validationRules.get(instance.constructor) || [];\n const errors: string[] = [];\n\n for (const rule of classRules) {\n if (!rule.validate(instance[rule.property])) {\n errors.push(rule.message);\n }\n }\n return errors;\n}\n\nclass UserProfile {\n @Required\n firstName: string;\n\n @Required\n lastName: string;\n\n age?: number;\n\n constructor(firstName: string, lastName: string, age?: number) {\n this.firstName = firstName;\n this.lastName = lastName;\n this.age = age;\n }\n}\n\nconst user1 = new UserProfile("John", "Doe", 30);\nconsole.log("Kullanıcı 1 doğrulama hataları:", validate(user1)); // []\n\nconst user2 = new UserProfile("", "Smith");\nconsole.log("Kullanıcı 2 doğrulama hataları:", validate(user2)); // ["firstName gereklidir."]\n\nconst user3 = new UserProfile("Alice", "");\nconsole.log("Kullanıcı 3 doğrulama hataları:", validate(user3)); // ["lastName gereklidir."]
Required
decorator'ı, doğrulama kuralını merkezi bir validationRules
haritasına kaydeder. Ayrı bir validate
fonksiyonu daha sonra bu metadatayı kullanarak örneği çalışma zamanında kontrol eder. Bu desen, doğrulama mantığını veri tanımından ayırarak yeniden kullanılabilir ve temiz hale getirir.
5. Parametre Decorator'ları
Parametre decorator'ları, bir sınıf yapıcısı veya metot içindeki parametrelere uygulanır. Üç argüman alırlar: hedef nesne (statik üyeler için yapılandırıcı fonksiyon; örnek üyeler için sınıfın prototipi), metodun adı (veya yapılandırıcı parametreleri için undefined
) ve fonksiyonun parametre listesindeki parametrenin sıra indeksi.
İmza:
function ParameterDecorator(target: Object, propertyKey: string | symbol | undefined, parameterIndex: number) { ... }
Dönüş Değeri:
Parametre decorator'ları herhangi bir değer döndüremez. Özellik decorator'ları gibi, birincil rolleri parametre hakkında metadata eklemektir.
Kullanım Alanları:
- Bağımlılık enjeksiyonu için parametre türlerini kaydetme (ör. Angular'daki
@Inject()
). - Belirli parametrelere doğrulama veya dönüşüm uygulama.
- Web framework'lerinde API istek parametreleri hakkında metadata çıkarma.
Parametre Decorator'ı Örneği: İstek Verisi Enjekte Etme
Bir web framework'ünün, bir metot parametresine bir istekten kullanıcı ID'si gibi belirli verileri enjekte etmek için parametre decorator'larını nasıl kullanabileceğini simüle edelim.
interface ParameterMetadata {\n index: number;\n key: string | symbol;\n resolver: (request: any) => any;\n}\n\nconst parameterResolvers: Map<Function, Map<string | symbol, ParameterMetadata[]>> = new Map();\n\nfunction RequestParam(paramName: string) {\n return function (target: Object, propertyKey: string | symbol | undefined, parameterIndex: number) {\n const targetKey = propertyKey || "constructor";\n let methodResolvers = parameterResolvers.get(target.constructor);\n if (!methodResolvers) {\n methodResolvers = new Map();\n parameterResolvers.set(target.constructor, methodResolvers);\n }\n const paramMetadata = methodResolvers.get(targetKey) || [];\n paramMetadata.push({\n index: parameterIndex,\n key: targetKey,\n resolver: (request: any) => request[paramName]\n });\n methodResolvers.set(targetKey, paramMetadata);\n };\n}\n\n// Çözümlenmiş parametrelerle bir metodu çağırmak için varsayımsal bir framework fonksiyonu\nfunction executeWithParams(instance: any, methodName: string, request: any) {\n const classResolvers = parameterResolvers.get(instance.constructor);\n if (!classResolvers) {\n return (instance[methodName] as Function).apply(instance, []);\n }\n const methodParamMetadata = classResolvers.get(methodName);\n if (!methodParamMetadata) {\n return (instance[methodName] as Function).apply(instance, []);\n }\n\n const args: any[] = Array(methodParamMetadata.length);\n for (const meta of methodParamMetadata) {\n args[meta.index] = meta.resolver(request);\n }\n return (instance[methodName] as Function).apply(instance, args);\n}\n\nclass UserController {\n getUser(@RequestParam("id") userId: string, @RequestParam("token") authToken?: string) {\n console.log(`Kullanıcı getiriliyor, ID: ${userId}, Token: ${authToken || "N/A"}`);\n return { id: userId, name: "Jane Doe" };\n }\n\n deleteUser(@RequestParam("id") userId: string) {\n console.log(`Kullanıcı siliniyor, ID: ${userId}`);\n return { status: "deleted", id: userId };\n }\n}\n\nconst userController = new UserController();\n\n// Gelen bir isteği simüle et\nconst mockRequest = {\n id: "user123",\n token: "abc-123",\n someOtherProp: "xyz"\n};\n\nconsole.log("\n--- getUser yürütülüyor ---");\nexecuteWithParams(userController, "getUser", mockRequest);\n\nconsole.log("\n--- deleteUser yürütülüyor ---");\nexecuteWithParams(userController, "deleteUser", { id: "user456" });
Bu örnek, parametre decorator'larının gerekli metot parametreleri hakkında nasıl bilgi toplayabildiğini göstermektedir. Bir framework daha sonra bu toplanan metadatayı kullanarak metot çağrıldığında uygun değerleri otomatik olarak çözümleyebilir ve enjekte edebilir, bu da controller veya servis mantığını önemli ölçüde basitleştirir.
Decorator Kompozisyonu ve Yürütme Sırası
Decorator'lar çeşitli kombinasyonlarda uygulanabilir ve davranışlarını tahmin etmek ve beklenmedik sorunlardan kaçınmak için yürütme sıralarını anlamak çok önemlidir.
Tek Bir Hedef Üzerinde Birden Fazla Decorator
Tek bir bildirime (örneğin bir sınıf, metot veya özellik) birden fazla decorator uygulandığında, belirli bir sırada yürütülürler: değerlendirmeleri için aşağıdan yukarıya veya sağdan sola. Ancak sonuçları ters sırada uygulanır.
@DecoratorA\n@DecoratorB\nclass MyClass {\n // ...\n}\n
Burada, önce DecoratorB
değerlendirilir, sonra DecoratorA
. Eğer sınıfı değiştirirlerse (örneğin yeni bir yapılandırıcı döndürerek), DecoratorA
'dan gelen değişiklik, DecoratorB
'den gelen değişikliği sarar veya üzerine uygulanır.
Örnek: Metot Decorator'larını Zincirleme
İki metot decorator'ı düşünün: LogCall
ve Authorization
.
function LogCall(target: any, propertyKey: string, descriptor: PropertyDescriptor) {\n const originalMethod = descriptor.value;\n descriptor.value = function (...args: any[]) {\n console.log(`[LOG] ${String(propertyKey)} çağrılıyor, argümanlar:`, args);\n const result = originalMethod.apply(this, args);\n console.log(`[LOG] ${String(propertyKey)} metodu döndü:`, result);\n return result;\n };\n return descriptor;\n}\n\nfunction Authorization(roles: string[]) {\n return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {\n const originalMethod = descriptor.value;\n descriptor.value = function (...args: any[]) {\n const currentUserRoles = ["admin"]; // Mevcut kullanıcı rollerini getirmeyi simüle et\n const authorized = roles.some(role => currentUserRoles.includes(role));\n if (!authorized) {\n console.warn(`[AUTH] ${String(propertyKey)} için erişim reddedildi. Gerekli roller: ${roles.join(", ")}`);\n throw new Error("Yetkisiz erişim");\n }\n console.log(`[AUTH] ${String(propertyKey)} için erişim verildi`);\n return originalMethod.apply(this, args);\n };\n return descriptor;\n };\n}\n\nclass SecureService {\n @LogCall\n @Authorization(["admin"])\n deleteSensitiveData(id: string) {\n console.log(`Hassas veri siliniyor, ID: ${id}`);\n return `Veri ID'si ${id} silindi.`;\n }\n\n @Authorization(["user"])\n @LogCall // Sıra burada değiştirildi\n fetchPublicData(query: string) {\n console.log(`Genel veri getiriliyor, sorgu: ${query}`);\n return `Sorgu için genel veri: ${query}`; \n }\n}\n\nconst service = new SecureService();\n\ntry {\n console.log("\n--- deleteSensitiveData çağrılıyor (Admin Kullanıcı) ---");\n service.deleteSensitiveData("record123");\n} catch (error: any) {\n console.error(error.message);\n}\n\ntry {\n console.log("\n--- fetchPublicData çağrılıyor (Admin Olmayan Kullanıcı) ---");\n // 'user' rolü gerektiren fetchPublicData'ya erişmeye çalışan admin olmayan bir kullanıcıyı simüle et\n const mockUserRoles = ["guest"]; // Bu, yetkilendirmede başarısız olur\n // Bunu dinamik hale getirmek için, mevcut kullanıcı rolleri için bir DI sistemine veya statik bağlama ihtiyacınız olur.\n // Basitlik adına, Authorization decorator'ının mevcut kullanıcı bağlamına erişimi olduğunu varsayıyoruz.\n // Demo amacıyla, Authorization decorator'ının her zaman 'admin' varsaydığını ayarlayalım, \n // böylece ilk çağrı başarılı olur ve ikincisi farklı yolları göstermek için başarısız olur.\n \n // fetchPublicData'nın başarılı olması için kullanıcı rolüyle yeniden çalıştırın.\n // Authorization'daki currentUserRoles'in şöyle olduğunu hayal edin: ['user']\n // Bu örnek için, basit tutalım ve sıra etkisini gösterelim.\n service.fetchPublicData("arama terimi"); // Bu, Auth -> Log'u çalıştırır\n} catch (error: any) {\n console.error(error.message);\n}\n\n/* deleteSensitiveData için beklenen çıktı:\n[AUTH] deleteSensitiveData için erişim verildi\n[LOG] deleteSensitiveData çağrılıyor, argümanlar: [ 'record123' ]\nHassas veri siliniyor, ID: record123\n[LOG] deleteSensitiveData metodu döndü: Veri ID'si record123 silindi.\n*/\n\n/* fetchPublicData için beklenen çıktı (kullanıcının 'user' rolü varsa):\n[LOG] fetchPublicData çağrılıyor, argümanlar: [ 'arama terimi' ]\n[AUTH] fetchPublicData için erişim verildi\nGenel veri getiriliyor, sorgu: arama terimi\n[LOG] fetchPublicData metodu döndü: Sorgu için genel veri: arama terimi\n*/
Sıraya dikkat edin: deleteSensitiveData
için önce Authorization
(alttaki) çalışır, sonra LogCall
(üstteki) onu sarar. Authorization
'ın iç mantığı önce yürütülür. fetchPublicData
için önce LogCall
(alttaki) çalışır, sonra Authorization
(üstteki) onu sarar. Bu, LogCall
aspect'inin Authorization
aspect'inin dışında olacağı anlamına gelir. Bu fark, loglama veya hata işleme gibi kesişen ilgiler için kritiktir, çünkü yürütme sırası davranışı önemli ölçüde etkileyebilir.
Farklı Hedefler için Yürütme Sırası
Bir sınıf, üyeleri ve parametrelerin hepsinde decorator'lar olduğunda, yürütme sırası iyi tanımlanmıştır:
- Parametre Decorator'ları önce, her parametre için son parametreden ilkine doğru uygulanır.
- Sonra, her üye için Metot, Erişimci veya Özellik Decorator'ları uygulanır.
- Son olarak, Sınıf Decorator'ları sınıfın kendisine uygulanır.
Her kategori içinde, aynı hedef üzerindeki birden fazla decorator aşağıdan yukarıya (veya sağdan sola) uygulanır.
Örnek: Tam Yürütme Sırası
function log(message: string) {\n return function (target: any, propertyKey: string | symbol | undefined, descriptorOrIndex?: PropertyDescriptor | number) {\n if (typeof descriptorOrIndex === 'number') {\n console.log(`Parametre Decorator'ı: ${message}, ${String(propertyKey || "constructor")} metodunun #${descriptorOrIndex} parametresinde`);\n } else if (typeof propertyKey === 'string' || typeof propertyKey === 'symbol') {\n if (descriptorOrIndex && 'value' in descriptorOrIndex && typeof descriptorOrIndex.value === 'function') {\n console.log(`Metot/Erişimci Decorator'ı: ${message}, ${String(propertyKey)} üzerinde`);\n } else {\n console.log(`Özellik Decorator'ı: ${message}, ${String(propertyKey)} üzerinde`);\n }\n } else {\n console.log(`Sınıf Decorator'ı: ${message}, ${target.name} üzerinde`);\n }\n return descriptorOrIndex; // Metot/erişimci için tanımlayıcıyı, diğerleri için undefined döndür\n };\n}\n\n@log("Sınıf Seviyesi D")\n@log("Sınıf Seviyesi C")\nclass MyDecoratedClass {\n @log("Statik Özellik A")\n static staticProp: string = "";\n\n @log("Örnek Özellik B")\n instanceProp: number = 0;\n\n @log("Metot D")\n @log("Metot C")\n myMethod(\n @log("Parametre Z") paramZ: string,\n @log("Parametre Y") paramY: number\n ) {\n console.log("myMethod metodu yürütüldü.");\n }\n\n @log("Getter/Setter F")\n get myAccessor() {\n return "";\n }\n\n set myAccessor(value: string) {\n //...\n }\n\n constructor() {\n console.log("Yapıcı yürütüldü.");\n }\n}\n\nnew MyDecoratedClass();\n// Metot decorator'ını tetiklemek için metodu çağır\nnew MyDecoratedClass().myMethod("hello", 123);\n\n/* Tahmini Çıktı Sırası (belirli TypeScript sürümüne ve derlemeye bağlı olarak yaklaşık):\nParametre Decorator'ı: Parametre Y, myMethod metodunun #1 parametresinde\nParametre Decorator'ı: Parametre Z, myMethod metodunun #0 parametresinde\nÖzellik Decorator'ı: Statik Özellik A, staticProp üzerinde\nÖzellik Decorator'ı: Örnek Özellik B, instanceProp üzerinde\nMetot/Erişimci Decorator'ı: Getter/Setter F, myAccessor üzerinde\nMetot/Erişimci Decorator'ı: Metot C, myMethod üzerinde\nMetot/Erişimci Decorator'ı: Metot D, myMethod üzerinde\nSınıf Decorator'ı: Sınıf Seviyesi C, MyDecoratedClass üzerinde\nSınıf Decorator'ı: Sınıf Seviyesi D, MyDecoratedClass üzerinde\nYapıcı yürütüldü.\nmyMethod metodu yürütüldü.\n*/
Kesin konsol log zamanlaması, bir yapıcının veya metodun ne zaman çağrıldığına bağlı olarak biraz değişebilir, ancak decorator fonksiyonlarının kendilerinin yürütüldüğü (ve dolayısıyla yan etkilerinin veya döndürülen değerlerinin uygulandığı) sıra yukarıdaki kuralları izler.
Decorator'larla Pratik Uygulamalar ve Tasarım Desenleri
Decorator'lar, özellikle reflect-metadata
polyfill'i ile birlikte, metadata odaklı programlamanın yeni bir alanını açar. Bu, tekrarlayan kodları ve kesişen ilgileri soyutlayan güçlü tasarım desenlerine olanak tanır.
1. Bağımlılık Enjeksiyonu (DI)
Decorator'ların en belirgin kullanımlarından biri Bağımlılık Enjeksiyonu framework'lerindedir (Angular'ın @Injectable()
, @Component()
vb. veya NestJS'in kapsamlı DI kullanımı gibi). Decorator'lar, bağımlılıkları doğrudan yapıcılarda veya özelliklerde bildirmenize olanak tanır, bu da framework'ün doğru servisleri otomatik olarak başlatmasını ve sağlamasını sağlar.
Örnek: Basitleştirilmiş Servis Enjeksiyonu
import "reflect-metadata"; // emitDecoratorMetadata için gerekli\n\nconst INJECTABLE_METADATA_KEY = Symbol("injectable");\nconst INJECT_METADATA_KEY = Symbol("inject");\n\nfunction Injectable() {\n return function (target: Function) {\n Reflect.defineMetadata(INJECTABLE_METADATA_KEY, true, target);\n };\n}\n\nfunction Inject(token: any) {\n return function (target: Object, propertyKey: string | symbol, parameterIndex: number) {\n const existingInjections: any[] = Reflect.getOwnMetadata(INJECT_METADATA_KEY, target, propertyKey) || [];\n existingInjections[parameterIndex] = token;\n Reflect.defineMetadata(INJECT_METADATA_KEY, existingInjections, target, propertyKey);\n };\n}\n\nclass Container {\n private static instances = new Map<any, any>();\n\n static resolve<T>(target: { new (...args: any[]): T }): T {\n if (Container.instances.has(target)) {\n return Container.instances.get(target);\n }\n\n const isInjectable = Reflect.getMetadata(INJECTABLE_METADATA_KEY, target);\n if (!isInjectable) {\n throw new Error(`${target.name} sınıfı @Injectable olarak işaretlenmemiş.`);\n }\n\n // Yapıcı parametrelerinin tiplerini al (emitDecoratorMetadata gerektirir)\n const paramTypes: any[] = Reflect.getMetadata("design:paramtypes", target) || [];\n const explicitInjections: any[] = Reflect.getMetadata(INJECT_METADATA_KEY, target) || [];\n\n const dependencies = paramTypes.map((paramType, index) => {\n // Sağlanmışsa açık @Inject token'ını kullan, aksi takdirde tipi çıkar\n const token = explicitInjections[index] || paramType;\n if (token === undefined) {\n throw new Error(`${target.name} için ${index} indeksindeki parametre çözümlenemiyor. Döngüsel bir bağımlılık veya açık @Inject olmadan ilkel bir tip olabilir.`);\n }\n return Container.resolve(token);\n });\n\n const instance = new target(...dependencies);\n Container.instances.set(target, instance);\n return instance;\n }\n}\n\n// Servisleri tanımla\n@Injectable()\nclass DatabaseService {\n connect() {\n console.log("Veritabanına bağlanılıyor...");\n return "DB Bağlantısı";\n }\n}\n\n@Injectable()\nclass AuthService {\n private db: DatabaseService;\n\n constructor(db: DatabaseService) {\n this.db = db;\n }\n\n login() {\n console.log(`AuthService: ${this.db.connect()} kullanılarak kimlik doğrulanıyor`);\n return "Kullanıcı giriş yaptı";\n }\n}\n\n@Injectable()\nclass UserService {\n private authService: AuthService;\n private dbService: DatabaseService; // Özel bir decorator veya framework özelliği kullanarak özellik üzerinden enjekte etme örneği\n\n constructor(@Inject(AuthService) authService: AuthService,\n @Inject(DatabaseService) dbService: DatabaseService) {\n this.authService = authService;\n this.dbService = dbService;\n }\n\n getUserProfile() {\n this.authService.login();\n this.dbService.connect();\n console.log("UserService: Kullanıcı profili getiriliyor...");\n return { id: 1, name: "Global User" };\n }\n}\n\n// Ana servisi çöz\nconsole.log("--- UserService çözümleniyor ---");\nconst userService = Container.resolve(UserService);\nconsole.log(userService.getUserProfile());\n\nconsole.log("\n--- AuthService çözümleniyor (önbellekten gelmeli) ---");\nconst authService = Container.resolve(AuthService);\nauthService.login();
Bu ayrıntılı örnek, @Injectable
ve @Inject
decorator'larının reflect-metadata
ile birleştiğinde özel bir Container
'ın bağımlılıkları otomatik olarak nasıl çözümleyip sağlayabildiğini göstermektedir. TypeScript tarafından otomatik olarak yayılan (emitDecoratorMetadata
true olduğunda) design:paramtypes
metadatası burada kesinlikle kritiktir.
2. Aspect-Oriented Programlama (AOP)
AOP, birden çok sınıf ve modülü kesen kesişen ilgilerin (ör. loglama, güvenlik, işlemler) modülerleştirilmesine odaklanır. Decorator'lar, TypeScript'te AOP kavramlarını uygulamak için mükemmel bir uyum sağlar.
Örnek: Metot Decorator'ı ile Loglama
LogCall
decorator'ını yeniden ele alırsak, bu mükemmel bir AOP örneğidir. Herhangi bir metoda, metodun orijinal kodunu değiştirmeden loglama davranışı ekler. Bu, "ne yapılacağını" (iş mantığı) "nasıl yapılacağından" (loglama, performans izleme vb.) ayırır.
function LogMethod(target: any, propertyKey: string, descriptor: PropertyDescriptor) {\n const originalMethod = descriptor.value;\n descriptor.value = function (...args: any[]) {\n console.log(`[LOG AOP] Metoda giriliyor: ${String(propertyKey)}, argümanlar:`, args);\n try {\n const result = originalMethod.apply(this, args);\n console.log(`[LOG AOP] Metottan çıkılıyor: ${String(propertyKey)}, sonuç:`, result);\n return result;\n } catch (error: any) {\n console.error(`[LOG AOP] ${String(propertyKey)} metodunda hata:`, error.message);\n throw error;\n }\n };\n return descriptor;\n}\n\nclass PaymentProcessor {\n @LogMethod\n processPayment(amount: number, currency: string) {\n if (amount <= 0) {\n throw new Error("Ödeme tutarı pozitif olmalıdır.");\n }\n console.log(`${amount} ${currency} tutarında ödeme işleniyor...`);\n return `${amount} ${currency} tutarındaki ödeme başarıyla işlendi.`;\n }\n\n @LogMethod\n refundPayment(transactionId: string) {\n console.log(`İşlem ID'si için ödeme iadesi yapılıyor: ${transactionId}...`);\n return `${transactionId} için iade başlatıldı.`;\n }\n}\n\nconst processor = new PaymentProcessor();\nprocessor.processPayment(100, "USD");\ntry {\n processor.processPayment(-50, "EUR");\n} catch (error: any) {\n console.error("Yakalanan hata:", error.message);\n}
Bu yaklaşım, PaymentProcessor
sınıfını tamamen ödeme mantığına odaklı tutarken, LogMethod
decorator'ı loglamanın kesişen ilgisini ele alır.
3. Doğrulama ve Dönüşüm
Decorator'lar, doğrulama kurallarını doğrudan özellikler üzerinde tanımlamak veya serileştirme/deserileştirme sırasında veriyi dönüştürmek için inanılmaz derecede kullanışlıdır.
Örnek: Özellik Decorator'ları ile Veri Doğrulaması
Daha önceki @Required
örneği bunu zaten göstermişti. İşte sayısal bir aralık doğrulaması ile başka bir örnek.
interface FieldValidationRule {\n property: string | symbol;\n validator: (value: any) => boolean;\n message: string;\n}\n\nconst fieldValidationRules = new Map<Function, FieldValidationRule[]>();\n\nfunction addValidationRule(target: Object, propertyKey: string | symbol, validator: (value: any) => boolean, message: string) {\n const rules = fieldValidationRules.get(target.constructor) || [];\n rules.push({ property: propertyKey, validator, message });\n fieldValidationRules.set(target.constructor, rules);\n}\n\nfunction IsPositive(target: Object, propertyKey: string | symbol) {\n addValidationRule(target, propertyKey, (value: number) => value > 0, `${String(propertyKey)} pozitif bir sayı olmalıdır.`);\n}\n\nfunction MaxLength(maxLength: number) {\n return function (target: Object, propertyKey: string | symbol) {\n addValidationRule(target, propertyKey, (value: string) => value.length <= maxLength, `${String(propertyKey)} en fazla ${maxLength} karakter uzunluğunda olmalıdır.`);\n };\n}\n\nclass Product {\n @MaxLength(50)\n name: string;\n\n @IsPositive\n price: number;\n\n constructor(name: string, price: number) {\n this.name = name;\n this.price = price;\n }\n\n static validate(instance: any): string[] {\n const errors: string[] = [];\n const rules = fieldValidationRules.get(instance.constructor) || [];\n for (const rule of rules) {\n if (!rule.validator(instance[rule.property])) {\n errors.push(rule.message);\n }\n }\n return errors;\n }\n}\n\nconst product1 = new Product("Laptop", 1200);\nconsole.log("Ürün 1 hataları:", Product.validate(product1)); // []\n\nconst product2 = new Product("Test amacıyla elli karakter sınırını aşan çok uzun ürün adı", 50);\nconsole.log("Ürün 2 hataları:", Product.validate(product2)); // ["name en fazla 50 karakter uzunluğunda olmalıdır."]\n\nconst product3 = new Product("Kitap", -10);\nconsole.log("Ürün 3 hataları:", Product.validate(product3)); // ["price pozitif bir sayı olmalıdır."]
Bu kurulum, model özellikleriniz üzerinde doğrulama kurallarını bildirimsel olarak tanımlamanıza olanak tanır, bu da veri modellerinizi kısıtlamaları açısından kendi kendini tanımlar hale getirir.
En İyi Uygulamalar ve Dikkat Edilmesi Gerekenler
Decorator'lar güçlü olsalar da, akıllıca kullanılmalıdırlar. Yanlış kullanımları, hata ayıklaması veya anlaşılması daha zor olan kodlara yol açabilir.
Decorator'ları Ne Zaman Kullanmalı (ve Ne Zaman Kullanmamalı)
- Kullanım alanları:
- Kesişen ilgiler: Loglama, önbelleğe alma, yetkilendirme, işlem yönetimi.
- Metadata bildirimi: ORM'ler için şema tanımlama, doğrulama kuralları, DI yapılandırması.
- Framework entegrasyonu: Metadata'dan yararlanan framework'ler oluştururken veya kullanırken.
- Tekrarlayan kodları azaltma: Tekrarlayan kod desenlerini soyutlama.
- Kaçınılması gereken durumlar:
- Basit fonksiyon çağrıları: Eğer basit bir fonksiyon çağrısı aynı sonucu açıkça elde edebiliyorsa, onu tercih edin.
- İş mantığı: Decorator'lar temel iş mantığını tanımlamamalı, onu artırmalıdır.
- Aşırı karmaşıklık: Bir decorator kullanmak kodu daha az okunabilir veya test edilmesi daha zor hale getiriyorsa, yeniden düşünün.
Performans Etkileri
Decorator'lar derleme zamanında (veya transpiled edilirse JavaScript çalışma zamanında tanım zamanında) yürütülür. Dönüşüm veya metadata toplama, her çağrıda değil, sınıf/metot tanımlandığında gerçekleşir. Bu nedenle, decorator'ları *uygulamanın* çalışma zamanı performans etkisi minimum düzeydedir. Ancak, decorator'larınızın *içindeki mantık*, özellikle her metot çağrısında maliyetli işlemler gerçekleştiriyorlarsa (ör. bir metot decorator'ı içindeki karmaşık hesaplamalar), bir performans etkisine sahip olabilir.
Sürdürülebilirlik ve Okunabilirlik
Decorator'lar, doğru kullanıldığında, tekrarlayan kodları ana mantığın dışına taşıyarak okunabilirliği önemli ölçüde artırabilir. Ancak, karmaşık, gizli dönüşümler gerçekleştiriyorlarsa, hata ayıklama zorlaşabilir. Decorator'larınızın iyi belgelendiğinden ve davranışlarının öngörülebilir olduğundan emin olun.
Deneysel Durum ve Decorator'ların Geleceği
TypeScript decorator'larının Aşama 3 TC39 teklifine dayandığını tekrar belirtmek önemlidir. Bu, spesifikasyonun büyük ölçüde kararlı olduğu ancak resmi ECMAScript standardının bir parçası olmadan önce hala küçük değişikliklere uğrayabileceği anlamına gelir. Angular gibi framework'ler, nihai standardizasyonlarına güvenerek onları benimsemiştir. Bu, yaygın olarak benimsenmeleri göz önüne alındığında, önemli kırıcı değişikliklerin olası olmamasına rağmen, belirli bir risk seviyesi anlamına gelir.
TC39 teklifi gelişti. TypeScript'in mevcut uygulaması, teklifin daha eski bir sürümüne dayanmaktadır. "Eski Decorator'lar" ile "Standart Decorator'lar" arasında bir ayrım vardır. Resmi standart geldiğinde, TypeScript muhtemelen uygulamasını güncelleyecektir. Framework kullanan çoğu geliştirici için bu geçiş, framework'ün kendisi tarafından yönetilecektir. Kütüphane yazarları için, eski ve gelecekteki standart decorator'lar arasındaki ince farkları anlamak gerekli olabilir.
emitDecoratorMetadata
Derleyici Seçeneği
Bu seçenek, tsconfig.json
'da true
olarak ayarlandığında, TypeScript derleyicisine belirli tasarım zamanı tür metadatasını derlenmiş JavaScript'e yaymasını söyler. Bu metadata, yapılandırıcı parametrelerinin türünü (design:paramtypes
), metotların dönüş türünü (design:returntype
) ve özelliklerin türünü (design:type
) içerir.
Bu yayılan metadata, standart JavaScript çalışma zamanının bir parçası değildir. Genellikle reflect-metadata
polyfill'i tarafından tüketilir ve daha sonra Reflect.getMetadata()
fonksiyonları aracılığıyla erişilebilir hale getirilir. Bu, bir container'ın bir sınıfın gerektirdiği bağımlılıkların türlerini açık bir yapılandırma olmadan bilmesi gereken Bağımlılık Enjeksiyonu gibi gelişmiş desenler için kesinlikle kritiktir.
Decorator'larla Gelişmiş Desenler
Decorator'lar, daha da sofistike desenler oluşturmak için birleştirilebilir ve genişletilebilir.
1. Decorator'ları Dekore Etme (Yüksek Mertebeden Decorator'lar)
Diğer decorator'ları değiştiren veya birleştiren decorator'lar oluşturabilirsiniz. Bu daha az yaygındır ancak decorator'ların fonksiyonel doğasını gösterir.
// Bir metodun loglanmasını sağlayan ve aynı zamanda admin rolleri gerektiren bir decorator\nfunction AdminAndLoggedMethod() {\n return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {\n // Önce Authorization'ı uygula (içte)\n Authorization(["admin"])(target, propertyKey, descriptor);\n // Sonra LogCall'u uygula (dışta)\n LogCall(target, propertyKey, descriptor);\n\n return descriptor; // Değiştirilmiş tanımlayıcıyı döndür\n };\n}\n\nclass AdminPanel {\n @AdminAndLoggedMethod()\n deleteUserAccount(userId: string) {\n console.log(`Kullanıcı hesabı siliniyor: ${userId}`);\n return `Kullanıcı ${userId} silindi.`;\n }\n}\n\nconst adminPanel = new AdminPanel();\nadminPanel.deleteUserAccount("user007");\n/* Beklenen Çıktı (admin rolü varsayılarak):\n[AUTH] deleteUserAccount için erişim verildi\n[LOG] deleteUserAccount çağrılıyor, argümanlar: [ 'user007' ]\nKullanıcı hesabı siliniyor: user007\n[LOG] deleteUserAccount metodu döndü: Kullanıcı user007 silindi.\n*/
Burada, AdminAndLoggedMethod
bir decorator döndüren bir fabrikadır ve bu decorator içinde iki başka decorator uygular. Bu desen, karmaşık decorator kompozisyonlarını kapsülleyebilir.
2. Mixin'ler için Decorator Kullanımı
TypeScript mixin'leri uygulamak için başka yollar sunsa da, decorator'lar sınıflara yetenekleri bildirimsel bir şekilde enjekte etmek için kullanılabilir.
function ApplyMixins(constructors: Function[]) {\n return function (derivedConstructor: Function) {\n constructors.forEach(baseConstructor => {\n Object.getOwnPropertyNames(baseConstructor.prototype).forEach(name => {\n Object.defineProperty(\n derivedConstructor.prototype,\n name,\n Object.getOwnPropertyDescriptor(baseConstructor.prototype, name) || Object.create(null)\n );\n });\n });\n };\n}\n\nclass Disposable {\n isDisposed: boolean = false;\n dispose() {\n this.isDisposed = true;\n console.log("Nesne yok edildi.");\n }\n}\n\nclass Loggable {\n log(message: string) {\n console.log(`[Loggable] ${message}`);\n }\n}\n\n@ApplyMixins([Disposable, Loggable])\nclass MyResource implements Disposable, Loggable {\n // Bu özellikler/metotlar decorator tarafından enjekte edilir\n isDisposed!: boolean;\n dispose!: () => void;\n log!: (message: string) => void;\n\n constructor(public name: string) {\n this.log(`Kaynak ${this.name} oluşturuldu.`);\n }\n\n cleanUp() {\n this.dispose();\n this.log(`Kaynak ${this.name} temizlendi.`);\n }\n}\n\nconst resource = new MyResource("NetworkConnection");\nconsole.log(`Yok edildi mi: ${resource.isDisposed}`);\nresource.cleanUp();\nconsole.log(`Yok edildi mi: ${resource.isBitti}`);
Bu @ApplyMixins
decorator'ı, temel yapılandırıcıların metotlarını ve özelliklerini türetilmiş sınıfın prototipine dinamik olarak kopyalar ve böylece işlevleri etkili bir şekilde "karıştırır".
Sonuç: Modern TypeScript Geliştirmeyi Güçlendirmek
TypeScript decorator'ları, metadata odaklı ve aspect-oriented programlamanın yeni bir paradigmasını sağlayan güçlü ve etkileyici bir özelliktir. Geliştiricilerin, sınıflara, metotlara, özelliklere, erişimcilere ve parametrelere temel mantıklarını değiştirmeden bildirimsel davranışlar eklemelerine, değiştirmelerine ve geliştirmelerine olanak tanır. Bu endişelerin ayrılması, daha temiz, daha sürdürülebilir ve oldukça yeniden kullanılabilir kodlara yol açar.
Bağımlılık enjeksiyonunu basitleştirmekten ve sağlam doğrulama sistemleri uygulamaktan, loglama ve performans izleme gibi kesişen ilgileri eklemeye kadar, decorator'lar birçok yaygın geliştirme zorluğuna zarif bir çözüm sunar. Deneysel durumları farkındalık gerektirse de, büyük framework'lerde yaygın olarak benimsenmeleri, pratik değerlerini ve gelecekteki ilgilerini göstermektedir.
TypeScript decorator'larında uzmanlaşarak, daha sağlam, ölçeklenebilir ve akıllı uygulamalar oluşturmanızı sağlayan önemli bir araç kazanırsınız. Onları sorumlu bir şekilde benimseyin, mekaniklerini anlayın ve TypeScript projelerinizde yeni bir bildirimsel güç seviyesinin kilidini açın.