decorator는 메타 프로그래밍에서 매우 유용하다. 그럼 메타 프로그래밍이란 무엇일까? => 소프트웨어 개발 생산성을 향상시키고 코드의 유지 보수성을 향상시킬 수 있는 강력한 도구이다. 즉, 사용자에겐 보여지지 않으며(물론 상황에 따라 보여지게 할 수 있지만) 타 개발자와함께 협업할 때 빛을 발하는 방식이다.
데코레이터를 사용하면 일반적으로 작업하지 않지만 대신 예를 들어 클래스 중 하나 또는 클래스의 메서드가 올바르게 사용되록 보장하거나 숨겨진 변환 등을 수행할 수 있다.
참고로 decorator는 class와 관련이 있다. 또한 선언할 때 class와 같이 대문자로 선언해주는 것이 좋다.
experimentalDecorators
설정을 true로 만들어줘야한다.at 이라고 부르며 TS가 인식하는 특수 식별자이다.
또한 decorator를 pointing을 해야하며 실행을 해선 안 된다.
decorator는 반드시 인자를 받는다.
데코레이터는 클래스가 정의될 때 실행된다. 인스턴스를 뽑아낼때 실행되는 것이 아니다.
function Logger(target: Function) {
console.log('logging...');
console.log(target);
}
@Logger
class Person{
name = '정우';
constructor() {
console.log('사람 객체를 만드는 중입니다...')
}
getLog() {
console.log('get Log');
}
}
const pers = new Person();
console.log(pers);
function Logger(logString: string) {
return function (target: Function) {
console.log(logString);
console.log(target);
};
}
@Logger('Logging - Person')
class Person{
name = '정우';
constructor() {
console.log('사람 객체를 만드는 중입니다...')
}
getLog() {
console.log('get Log');
}
}
const pers = new Person();
console.log(pers);
function WithTemplate(template: string, hookId: string) {
return function (constructor:any) {
const hookEl = document.getElementById(hookId);
const p = new constructor();
if (hookEl) {
hookEl.innerHTML = template;
hookEl.querySelector('h1')!.textContent = p.name;
}
}
}
// @Logger('Logging - Person')
@WithTemplate('<h1>My person Obj</h1>','app')
class Person{
name = '정우';
constructor() {
console.log('사람 객체를 만드는 중입니다...')
}
getLog() {
console.log('get Log');
}
}
function Logger(logString: string) {
return function (target: Function) {
console.log(logString);
console.log(target);
};
}
function WithTemplate(template: string, hookId: string) {
return function (constructor:any) {
const hookEl = document.getElementById(hookId);
const p = new constructor();
if (hookEl) {
hookEl.innerHTML = template;
hookEl.querySelector('h1')!.textContent = p.name;
}
}
}
@Logger('Logging - Person')
@WithTemplate('<h1>My person Obj</h1>','app')
class Person{
name = '정우';
constructor() {
console.log('사람 객체를 만드는 중입니다...')
}
getLog() {
console.log('get Log');
}
}
function Log(target: any, propertyName: string|Symbol) {
console.log('property decorator!');
console.log(target, propertyName);
}
class Product{
@Log
title: string;
private _price: number;
set price(val: number) {
if (val > 0) {
this._price = val;
} else {
throw new Error('값이 이상합')
}
}
constructor(t: string, p: number) {
this.title = t;
this._price = p;
}
getPriceWithText(tax: number) {
return this._price * (1 + tax);
}
}
1.은 해당 accessor가 가르키는 객체이다. 이때 TS는 해당 descorator(accessor)가 어디에 쓰일지 모르기 때문에 이때는 type이 any여야 맞다.
2.name은 accorr의 이름이다. 3.의 descriptor는 말 그대로 accessor의 속성을 좀 더 디테일하게 설명해주는 것이다.
function Log2(target:any, name:string, descriptor:PropertyDescriptor) {
console.log('Accessor decorator!');
console.log(target);
console.log(name);
console.log(descriptor);
}
class Product{
title: string;
private _price: number;
@Log2
set price(val: number) {
if (val > 0) {
this._price = val;
} else {
throw new Error('값이 이상합')
}
}
constructor(t: string, p: number) {
this.title = t;
this._price = p;
}
getPriceWithText(tax: number) {
return this._price * (1 + tax);
}
}
function Log3(target:any, name:string|Symbol, descriptor:PropertyDescriptor) {
console.log('Method decorator!');
console.log(target);
console.log(name);
console.log(descriptor);
}
class Product{
title: string;
private _price: number;
set price(val: number) {
if (val > 0) {
this._price = val;
} else {
throw new Error('값이 이상합')
}
}
@Log3
constructor(t: string, p: number) {
this.title = t;
this._price = p;
}
getPriceWithText(tax: number) {
return this._price * (1 + tax);
}
}
function Log4(target:any, name:string|Symbol, position:number) {
console.log('parameter decorator!');
console.log(target);
console.log(name);
console.log(position);
}
class Product{
title: string;
private _price: number;
set price(val: number) {
if (val > 0) {
this._price = val;
} else {
throw new Error('값이 이상합')
}
}
constructor(t: string, p: number) {
this.title = t;
this._price = p;
}
getPriceWithText(@Log4 tax: number) {
return this._price * (1 + tax);
}
}