Typescript Decorator Nedir?

Mehmet Ozan Turhan
4 min readOct 29, 2019

--

Bu yazıda örnek bir uygulama üzerinden typescript decoratorlerini inceleyeceğim.

Öncelikle kısaca decoratorlerin ne olduğunu anlamaya çalışalım.

Decoratorler class, method, property ve parametrelere eklenebilen özel bir declaration türüdür. Java ve C# gibi dillerde sıkça karşımıza annotation olarak çıkmakta ve kullanmaktayız. Örneğin Symfony’de controller methodlarının üzerinde tanımladığımız @Route annotation’ı gibi.

Typescript’in dökümanlarında decoratorlerin sonraki sürümlerde değişebilecek deneysel bir özellik olduğu not olarak düşülmüştür.

Decoratorler “@expression(…)” formatında kullanılmaktadır. Aslında bu decoratorler örneklerde de göreceğimiz üzere typescript fonksiyonlarıdır.

Decoratorleri kullanabilmemiz için typescriptin experimentalDecorators özelliğini kullanarak kodu derlememiz veya tsconfig dosyasından aktif hale getirmemiz gerekmektedir.

Derleme:

tsc --target ES5 --experimentalDecorators

tsconfig.json:

{ 
"compilerOptions": {
....,
"experimentalDecorators": true,
}
}

Decoratorler

Bir declaration’a (class, method, property yada parameter) decorator tanımlamak için decoratorlerin lib.es5.d.ts dosyasındaki declaration tanımlarını aşağıdaki resimden inceleyelim.

Bir decoratore parametreler göndermek istediğimiz zaman decorator factory kullanmamız gerekmektedir. Class decoratorlerinden bahsederken buna değineceğim.

Yazının başında da söylediğim gibi biz decoratorleri bir örnek üzerinden inceleyeceğiz. Örneğe geçmeden önce class decorator üzerinden kısaca nasıl tanımlandıklarına bakalım.

Decoratorler Nasıl Tanınlanır?

Aşağıdan class, method, property ve parameter decorator tanımlarını inceleyebilirsiniz:

Class Decorator

Class decorator tanımlamak için şöyle bir fonksiyon yazmamız yeterlidir:

// tanımı
function ClassDecorator(target) {
// target ile istediğiniz işlemi gerçekleştirin. Burada target TestClass olmaktadır.
}
// kullanımı
@ClassDecorator
class TestClass() {
}

Eğer bir decoratore parametre vermek istiyorsak ozaman aşağdaki gibi bir factory oluşturmalıyız:

// tanımı
function ClassDecorator(value: string) { // decorator factory
return function(target) { // decorator
// target ve value ile istediğiniz işlemi gerçekleştirin
}
}
// kullanımı
@ClassDecorator('deger')
class TestClass() {
}

Decoratorle ilgili daha ayrıntılı bilgiye kaynaklarda da verdiğim typescript dökümanından ulaşabilirsiniz.

Decorator Kullanacağımız Örnek Uygulama

Angular kullanarak jsonplaceholder.typicode.com adlı sitenin sağladığı rest api üzerinden bir uygulama geliştireceğiz. Yapacağımız uygulamada http requestlerini decoratorler üzerinden gerçekleştireceğimiz bir yapı kuracağız.

Burada sadece uygulamamızda ihtiyaç duyduğumuz decoratorleri inceleyeceğim. Stackblitz ve github üzerinden kodu daha detaylı incelebilirsiniz.

Tanımlayacağımız Decoratorler

  • Client
  • Request
  • UrlParam

Client Decorator

Class decorator olarak tanımlayacağız. Parametre olarak url alacak ve eklendiği classın url propertysini değiştirecek.

Kullanımı aşağıdaki gibi olacak:

user.service.ts

Etkilediği class ise RestClient isimli class olacak:

UrlParam Decorator

Bu decorator Request decoratoründe tanımlayacağımız endpointteki parametreleri belirtmek için kullanılacaktır.

Kullanımı aşağıda ki gibi olacak:

user.service.ts

Şimdi UrlParam decoratoru inceleyelim:

Yukarıdaki getUser methodundan yola çıkarak UrlParam decoratorünü anlamaya çalışalım.

getUser metodunda, her bir parametre için tanımladığımız UrlParam decoratorunun değerini, — getUser için id — “target” yani RestClient’ta, dolayısıyla onu referans alan UserService’te “url_parameters” adında bir metadata içerisine ekleyerek tanımlıyoruz.

Programı durdurup baktığımızda target yani RestClient’a yukarıda gördüğümüz üzere url_parameter adında bir array eklendiğini görmekteyiz. Bu array bize Request decoratörümüzde lazım olacak.

Request Decorator

Resim üzerinden gidecek olursak Request decoratorunun projemizde ki kullanım amacını daha rahat anlayabiliriz. Request endpoint ve http method olmak üzere iki parametre almaktadır. Eğer verdiğimiz endpoint bir url paramater alıyorsa bunu yukarıda da gördüğümüz gibi ‘users/:id’ şeklinde tanımlamamız gerecektir. Request decoratörü ilgili entpointe bizim verdiğimiz http method ile birlikte request gönderecek ve observable olarak sonuç döndürecektir.

Şimdi Request decoratorun kodunu inceleyelim:

Class ve property decoratorden farklı olarak method decorator, descriptor adında bir parametre almaktadır.

lib.es5.d.ts

Resimde de görüldüğü üzere descriptorda value adında bir değişken bulunmaktadır. Burada dikkat etmemiz gereken nokta şurası:

descriptor.value = function(...args: any[]) 

kodun bu kısmında aslında ifadem doğrusa getUser adlı metodu yeniden tanımlıyoruz.

getUser(@UrlParam('id') id: number): Observable<UserModel> { return null; }

Normalde yukarıdaki metod null bir değer döndürmekteyken biz descriptor.value üzerinden bu metodun yapacağı işi ve döndüreceği değeri manipüle etmiş gibi oluyoruz.

PropertyDescriptor ile ilgili daha fazla bilgi almak için typescript dökümanına bakabilirsiniz.

const urlParams = target[`url_parameters`];

Burada UrlParam decoratoru aracılığıyle set edilen parametreleri alıyoruz. getUser metodu için {key: ‘id’, index: 0} içeriyor olacak.

let url = `${this.url}${endpoint}`; // url oluşturuluyor.
if (urlParams) { // url parameter var mı kontrol ediliyor.
for (const param of urlParams) {
// var olan url parametreler ilgili metodun ilgili parametresine denk gelen değer ile değiştiriliyor.
url = url.replace(':' + param.key, args[ param.index ]);
}
}
const request = new HttpRequest(httpMethod, url); // request objesi oluşturuluyor
// http reqeusti oluşturulup dönüş değeri olarak veriliyor.
return this.httpClient.request(request).pipe(
map((response: HttpResponse<any>) => response.body)
);

Son olarak getUser metodunun resolver üzerinden nasıl çağırıldığını görelim:

Kodu stackblitz üzerinden inceleyebilirsiniz:

--

--