ECMAScript | TypeScript

Typescript의 Decorator - 1. Class Decorator

partner_jun 2017. 8. 13. 12:52

타입스크립트의 데코레이터는 자바에서의 어노테이션과 유사하게 메타데이터를 지정할 수 있다. 하지만 인터페이스로써 메타데이터만을 저장하는 자바와 달리 ES6의 데코레이터는 메소드나 프로퍼티를 직접 변경할 수 있다. 더 정확히 말하자면, TypeScript의 데코레이터는 파이썬의 데코레이터와 마찬가지로, 함수를 파라미터로 받는 함수를 쉽게 선언하는 일종의 Sugar Syntax다.

또, 어노테이션이 붙은 모든 클래스나 메소드, 프로퍼티를 찾을 수 있는 자바 리플렉션과 달리 TypeScript의 'reflect-metadata'에는 그런 기능이 없는 것으로 보인다. Angular의 app.module.ts 파일에 컴포넌트 클래스명을 직접 입력해야 하는 이유가 이것 때문이 아닐까 하는 추측을 해본다(추측만).


몇 가지 예제에 사용할 클래스는 아래와 같다.

class Car {
name: string;
price: number;
type: string;

constructor(name: string, price: number) {
this.name = name;
this.price = price;
}

}





1. 클래스 데코레이터

클래스 선언부 앞에 붙이고, 클래스의 프로퍼티를 변경하는 데코레이터다.

@ClassDecorator1
class Car {
name: string; ...

데코레이터도 자바에서의 어노테이션과 마찬가지로 '@데코레이터명'을 특정 위치에 기입한다.


function ClassDecorator1(constructor: any) {
console.log(constructor); // ƒ Car(name, price) {
// this.name = name;
// this.price = price;
// }
return <any>class extends constructor {
name = 'SM6';
color = 'black';
}
}

소스를 보면 알 수 있듯 생성자를 오버라이드하여 새로운 클래스를 반환한다.

이 코드에서는 생성자 파라미터를 any 타입으로 받았는데, 공식 홈페이지에 따르면 사실 any타입이 아니라

<T extends {new(...args:any[]):{}}>(constructor:T)

...args: any[] 파라미터를 받는 생성자를 가진 타입 T로 받아야 한다.


let myCar = new Car('SM5', 2000);
console.log(myCar); // class_1 {name: "SM6", price: 2000, color: "black"}

클래스 선언과 달리 color 프로퍼티가 추가된 것을 확인할 수 있다.




2. 파라미터를 받는 클래스 데코레이터

앵귤러의 @Component 데코레이터처럼 데코레이터에 파라미터를 입력받을 수도 있다.

@ClassDecorator2({someValue: 'hello!'})
class Car {
name: string; ...

파라미터를 입력하는 데코레이터도 자바의 어노테이션과 마찬가지로 ()안에 파라미터를 입력한다.


function ClassDecorator2(param: any) {
console.log(param); // {someValue: "hello!"}
return function(constructor: any) {
console.log(constructor); // ƒ Car(name, price) {
// this.name = name;
// this.price = price;
// }
return <any>class extends constructor {
someValue = param.someValue + ' world!';
}
}
}

첫 번째 예제와 비슷하지만 파라미터를 받는 데코레이터 선언부와 생성자를 받는 데코레이터 선언부를 따로 선언한다.


let myCar2 = new Car('SM5', 2000);
console.log(myCar2); // class_2 {name: "SM5", price: 2000, someValue: "hello! world!"}

데코레이터에 파라미터로 입력한 "hello!"와 데코레이터 내부의 프로퍼티 변경으로 Car 클래스의 객체에 {someValue: "hello! world!"} 가 입력된 것을 확인할 수 있다.