Türkçe

TypeScript decorator'larını keşfedin: Kod yapısını, yeniden kullanılabilirliği ve sürdürülebilirliği geliştirmek için güçlü bir metaprogramlama özelliği. Pratik örneklerle bunları nasıl etkili bir şekilde kullanacağınızı öğrenin.

TypeScript Decorator'ları: Metaprogramlamanın Gücünü Ortaya Çıkarma

TypeScript decorator'ları, kodunuzu metaprogramlama yetenekleriyle geliştirmek için güçlü ve zarif bir yol sunar. Davranış ve anotasyonları kodunuzun temel mantığını değiştirmeden enjekte etmenize olanak tanıyarak, sınıfları, metotları, özellikleri ve parametreleri tasarım zamanında değiştirme ve genişletme mekanizması sunarlar. Bu blog yazısı, her seviyeden geliştirici için kapsamlı bir rehber sunarak TypeScript decorator'larının inceliklerine dalacaktır. Decorator'ların ne olduğunu, nasıl çalıştıklarını, mevcut farklı türlerini, pratik örnekleri ve etkili kullanımları için en iyi pratikleri keşfedeceğiz. İster TypeScript'e yeni başlamış olun, ister deneyimli bir geliştirici olun, bu rehber sizi daha temiz, daha sürdürülebilir ve daha ifade gücü yüksek kodlar için decorator'lardan yararlanma bilgisiyle donatacaktır.

TypeScript Decorator'ları Nedir?

Özünde, TypeScript decorator'ları bir metaprogramlama biçimidir. Temelde bir veya daha fazla argüman alan (genellikle dekore edilen şey, örneğin bir sınıf, metot, özellik veya parametre) ve onu değiştirebilen veya yeni işlevsellik ekleyebilen fonksiyonlardır. Onları kodunuza eklediğiniz anotasyonlar veya nitelikler olarak düşünebilirsiniz. Bu anotasyonlar daha sonra kod hakkında meta veriler sağlamak veya davranışını değiştirmek için kullanılabilir.

Decorator'lar, `@` sembolü ve ardından bir fonksiyon çağrısı (örneğin, `@decoratorAdi()`) kullanılarak tanımlanır. Decorator fonksiyonu, uygulamanızın tasarım zamanı aşamasında çalıştırılacaktır.

Decorator'lar, Java, C# ve Python gibi dillerdeki benzer özelliklerden esinlenmiştir. Temel mantığınızı temiz tutarak ve meta veri veya değişiklik yönlerinizi özel bir yerde odaklayarak endişelerin ayrılmasını ve kodun yeniden kullanılabilirliğini teşvik etmenin bir yolunu sunarlar.

Decorator'lar Nasıl Çalışır?

TypeScript derleyicisi, decorator'ları tasarım zamanında çağrılan fonksiyonlara dönüştürür. Decorator fonksiyonuna geçirilen kesin argümanlar, kullanılan decorator türüne (sınıf, metot, özellik veya parametre) bağlıdır. Farklı decorator türlerini ve ilgili argümanlarını inceleyelim:

Bu argüman imzalarını anlamak, etkili decorator'lar yazmak için çok önemlidir.

Decorator Türleri

TypeScript, her biri belirli bir amaca hizmet eden birkaç tür decorator'ı destekler:

Pratik Örnekler

TypeScript'te decorator'ların nasıl kullanılacağını göstermek için bazı pratik örnekleri inceleyelim.

Sınıf Decorator'ı Örneği: Zaman Damgası Ekleme

Bir sınıfın her örneğine bir zaman damgası eklemek istediğinizi düşünün. Bunu başarmak için bir sınıf decorator'ı kullanabilirsiniz:


function addTimestamp<T extends { new(...args: any[]): {} }>(constructor: T) {
  return class extends constructor {
    timestamp = Date.now();
  };
}

@addTimestamp
class MyClass {
  constructor() {
    console.log('MyClass oluşturuldu');
  }
}

const instance = new MyClass();
console.log(instance.timestamp); // Çıktı: bir zaman damgası

Bu örnekte, `addTimestamp` decorator'ı sınıf örneğine bir `timestamp` özelliği ekler. Bu, orijinal sınıf tanımını doğrudan değiştirmeden değerli hata ayıklama veya denetim izi bilgileri sağlar.

Metot Decorator'ı Örneği: Metot Çağrılarını Kaydetme

Metot çağrılarını ve argümanlarını kaydetmek için bir metot decorator'ı kullanabilirsiniz:


function logMethod(target: any, key: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;

  descriptor.value = function (...args: any[]) {
    console.log(`[LOG] ${key} metodu şu argümanlarla çağrıldı:`, args);
    const result = originalMethod.apply(this, args);
    console.log(`[LOG] ${key} metodu şunu döndürdü:`, result);
    return result;
  };

  return descriptor;
}

class Greeter {
  @logMethod
  greet(message: string): string {
    return `Merhaba, ${message}!`;
  }
}

const greeter = new Greeter();
console.log(greeter.greet('Dünya'));
// Çıktı:
// [LOG] greet metodu şu argümanlarla çağrıldı: [ 'Dünya' ]
// [LOG] greet metodu şunu döndürdü: Merhaba, Dünya!

Bu örnek, bir `greet` metodunun her çağrıldığında, argümanları ve dönüş değeriyle birlikte kaydeder. Bu, daha karmaşık uygulamalarda hata ayıklama ve izleme için çok kullanışlıdır.

Özellik Decorator'ı Örneği: Doğrulama Ekleme

İşte temel doğrulama ekleyen bir özellik decorator'ı örneği:


function validate(target: any, key: string) {
  let value: any;

  const getter = function () {
    return value;
  };

  const setter = function (newValue: any) {
    if (typeof newValue !== 'number') {
      console.warn(`[UYARI] Geçersiz özellik değeri: ${key}. Bir sayı bekleniyordu.`);
      return;
    }
    value = newValue;
  };

  Object.defineProperty(target, key, {
    get: getter,
    set: setter,
    enumerable: true,
    configurable: true,
  });
}

class Person {
  @validate
  age: number; //  <- Doğrulamalı özellik
}

const person = new Person();
person.age = 'abc'; // Bir uyarı kaydeder
person.age = 30;   // Değeri ayarlar
console.log(person.age); // Çıktı: 30

Bu `validate` decorator'ında, atanan değerin bir sayı olup olmadığını kontrol ediyoruz. Değilse, bir uyarı kaydediyoruz. Bu basit bir örnektir ancak decorator'ların veri bütünlüğünü zorlamak için nasıl kullanılabileceğini gösterir.

Parametre Decorator'ı Örneği: Bağımlılık Enjeksiyonu (Basitleştirilmiş)

Tam teşekküllü bağımlılık enjeksiyonu framework'leri genellikle daha karmaşık mekanizmalar kullansa da, decorator'lar enjeksiyon için parametreleri işaretlemek için de kullanılabilir. Bu örnek basitleştirilmiş bir gösterimdir:


// Bu bir basitleştirmedir ve gerçek enjeksiyonu ele almaz. Gerçek DI daha karmaşıktır.
function Inject(service: any) {
  return function (target: any, propertyKey: string | symbol, parameterIndex: number) {
    // Servisi bir yerde saklayın (örneğin, statik bir özellikte veya bir haritada)
    if (!target.injectedServices) {
      target.injectedServices = {};
    }
    target.injectedServices[parameterIndex] = service;
  };
}

class MyService {
  doSomething() { /* ... */ }
}

class MyComponent {
  constructor(@Inject(MyService) private myService: MyService) {
    // Gerçek bir sistemde, DI konteyneri 'myService'i burada çözerdi.
    console.log('MyComponent şununla oluşturuldu:', myService.constructor.name); //Örnek
  }
}

const component = new MyComponent(new MyService());  // Servisi enjekte etme (basitleştirilmiş).

`Inject` decorator'ı bir parametrenin bir servis gerektirdiğini işaretler. Bu örnek, bir decorator'ın bağımlılık enjeksiyonu gerektiren parametreleri nasıl tanımlayabileceğini gösterir (ancak gerçek bir framework'ün servis çözümlemesini yönetmesi gerekir).

Decorator Kullanmanın Faydaları

Decorator Kullanımı için En İyi Pratikler

İleri Düzey Kavramlar

Decorator Fabrikaları

Decorator fabrikaları, decorator fonksiyonları döndüren fonksiyonlardır. Bu, decorator'larınıza argümanlar geçirmenizi sağlayarak onları daha esnek ve yapılandırılabilir hale getirir. Örneğin, doğrulama kurallarını belirtmenize olanak tanıyan bir doğrulama decorator fabrikası oluşturabilirsiniz:


function validate(minLength: number) {
  return function (target: any, key: string) {
    let value: string;

    const getter = function () {
      return value;
    };

    const setter = function (newValue: string) {
      if (typeof newValue !== 'string') {
        console.warn(`[UYARI] Geçersiz özellik değeri: ${key}. Bir dize bekleniyordu.`);
        return;
      }
      if (newValue.length < minLength) {
        console.warn(`[UYARI] ${key} en az ${minLength} karakter uzunluğunda olmalıdır.`);
        return;
      }
      value = newValue;
    };

    Object.defineProperty(target, key, {
      get: getter,
      set: setter,
      enumerable: true,
      configurable: true,
    });
  };
}

class Person {
  @validate(3) // Minimum 3 uzunlukla doğrula
  name: string;
}

const person = new Person();
person.name = 'Al';
console.log(person.name); // Bir uyarı kaydeder, değeri ayarlar.
person.name = 'Ali';
console.log(person.name); // Çıktı: Ali

Decorator fabrikaları, decorator'ları çok daha uyarlanabilir hale getirir.

Decorator'ları Birleştirme

Aynı öğeye birden fazla decorator uygulayabilirsiniz. Uygulandıkları sıra bazen önemli olabilir. Sıra, aşağıdan yukarıyadır (yazıldığı gibi). Örneğin:


function first() {
  console.log('first(): fabrika değerlendirildi');
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    console.log('first(): çağrıldı');
  }
}

function second() {
  console.log('second(): fabrika değerlendirildi');
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    console.log('second(): çağrıldı');
  }
}

class ExampleClass {
  @first()
  @second()
  method() {}
}

// Çıktı:
// second(): fabrika değerlendirildi
// first(): fabrika değerlendirildi
// second(): çağrıldı
// first(): çağrıldı

Fabrika fonksiyonlarının göründükleri sırayla değerlendirildiğine, ancak decorator fonksiyonlarının ters sırada çağrıldığına dikkat edin. Decorator'larınız birbirine bağlıysa bu sıralamayı anlayın.

Decorator'lar ve Metadata Yansıması

Decorator'lar, daha dinamik davranışlar elde etmek için meta veri yansımasıyla (örneğin, `reflect-metadata` gibi kütüphaneler kullanarak) el ele çalışabilir. Bu, örneğin, çalışma zamanında dekore edilmiş öğeler hakkında bilgi depolamanıza ve almanıza olanak tanır. Bu, özellikle framework'lerde ve bağımlılık enjeksiyon sistemlerinde yardımcı olur. Decorator'lar, sınıfları veya metotları meta verilerle donatabilir ve ardından bu meta verileri keşfetmek ve kullanmak için yansıma kullanılabilir.

Popüler Framework'lerde ve Kütüphanelerde Decorator'lar

Decorator'lar, birçok modern JavaScript framework'ünün ve kütüphanesinin ayrılmaz bir parçası haline gelmiştir. Uygulamalarını bilmek, framework'ün mimarisini ve çeşitli görevleri nasıl kolaylaştırdığını anlamanıza yardımcı olur.

Bu framework'ler ve kütüphaneler, decorator'ların kod organizasyonunu nasıl geliştirdiğini, yaygın görevleri nasıl basitleştirdiğini ve gerçek dünya uygulamalarında sürdürülebilirliği nasıl teşvik ettiğini gösterir.

Zorluklar ve Dikkat Edilmesi Gerekenler

Sonuç

TypeScript decorator'ları, kodunuzun yapısını, yeniden kullanılabilirliğini ve sürdürülebilirliğini önemli ölçüde iyileştirebilen güçlü bir metaprogramlama özelliğidir. Farklı decorator türlerini, nasıl çalıştıklarını ve kullanımları için en iyi pratikleri anlayarak, daha temiz, daha ifade gücü yüksek ve daha verimli uygulamalar oluşturmak için onlardan yararlanabilirsiniz. İster basit bir uygulama ister karmaşık bir kurumsal düzeyde sistem oluşturuyor olun, decorator'lar geliştirme iş akışınızı geliştirmek için değerli bir araç sunar. Decorator'ları benimsemek, kod kalitesinde önemli bir iyileşme sağlar. Geliştiriciler, Angular ve NestJS gibi popüler framework'ler içinde decorator'ların nasıl entegre olduğunu anlayarak, ölçeklenebilir, sürdürülebilir ve sağlam uygulamalar oluşturmak için tam potansiyellerinden yararlanabilirler. Anahtar, amaçlarını ve uygun bağlamlarda nasıl uygulanacaklarını anlamak, faydaların olası dezavantajlardan daha ağır basmasını sağlamaktır.

Decorator'ları etkili bir şekilde uygulayarak, kodunuzu daha iyi bir yapı, sürdürülebilirlik ve verimlilikle geliştirebilirsiniz. Bu rehber, TypeScript decorator'larının nasıl kullanılacağına dair kapsamlı bir genel bakış sunmaktadır. Bu bilgiyle, daha iyi ve daha sürdürülebilir TypeScript kodu oluşturma gücüne sahipsiniz. İleriye gidin ve dekore edin!