데코레이터
: 데코레이터 함수를 뜻하며, 코드에 첨부하여 메서드 및 함수에 추가적인 기능을 더하는 선언
: 비슷한 개념으로 자바의 어노테이션이 있는데, 타입스크립트에서는 파이썬의 데코레이터와 비슷한 개념이다
- 타입스크립트에서의 데코레이터
: 클래스 선언, 메서드, 접근자, 프로퍼티, 매개변수에 첨부할 수 있는 특수한 선언
: 파이썬과 동일하게 런타임에 실행되며, 메서드나 클래스 인스턴스가 만들어질때 실행된다
: 데코레이터는 클래스 또는 클래스 내부의 생성자, 프로퍼티, 접근자, 메서드, 매개변수에만 장식될 수 있다
타입스크립트와 데코레이터
: 타입스크립트와 ES6에 클래스가 도입됨에 따라 클래스 및 클래스 멤버에 어노테이션을 달거나 수정하기 위한 기능으로 제공
: 현재는 타입스크립트의 실험적인 기능으로 이용가능하며, 향후 릴리즈에서 변경이 될 수 있다
- 데코레이터 기능 활성화
: tsconfig.json 수정
{
"compilerOptions": {
"target": "ES6",
"experimentalDecorators": true
}
}
데코레이터 팩토리 (Decorator Factory)
: 데코레이터 함수를 감싸는 래퍼 함수
: 보통 데코레이터가 선언에 적용되는 방식을 수정할때 사용
: 즉, 단순히 데코레이터가 런타임에 호출할 표현식을 반환하는 함수
- 데코레이터 함수
: 실질적인 데코레이터의 로직
: target (현재 타겟), key (속성 이름), descriptor (설명)이 파라미터로 전달되며, 어떤 멤버를 장식했느냐에 따라 인수가 달라질 수 있다
- 예시
function color(value: string) { // 데코레이터 팩토리
return function (target) { // 데코레이터
// 'target'과 'value' 변수를 가지고 무언가를 수행합니다.
}
}
데코레이터 합성 (Decorator Composition)
: 데코레이터 선언 시 여러 데코레이터를 적용할 수 있다
: 여러 데코레이터가 단일 선언에 적용되는 경우 수학의 합성 함수와 비슷하게 적용됨
: 각 데코레이터의 표현은 위에서 아래로 평가되고, 결과는 아래에서 위로 함수로 호출됨
- 단일 행
@f @g x
- 여러 행
@f
@g
x
- 예시
function f() {
console.log("f(): evaluated");
return function (target, propertyKey: string, descriptor: PropertyDescriptor) {
console.log("f(): called");
}
}
function g() {
console.log("g(): evaluated");
return function (target, propertyKey: string, descriptor: PropertyDescriptor) {
console.log("g(): called");
}
}
class C {
@f()
@g()
method() {}
}
// 결과
f(): evaluated
g(): evaluated
g(): called
f(): called
클래스 데코레이터
: 클래스 선언 직전에 선언되는 데코레이터
: 클래스 생성자에 적용되며 클래스 정의를 관찰, 수정 또는 교체하는데 사용
: 선언 파일이나 주변 컨텍스트 (ex) 선언 클래스)에서 사용할 수 없음
- 매개변수
: constructor (생성자 함수가 데코레이터로 자동 전달하므로 명시하지 않아도 됨)
- 반환 값
: class, void
- 예시
function classDecorator<T extends { new (...args: any[]): {} }>(constructor: T) {
// Test 클래스의 constructor를 상속해서 new Test() 가 되면 추가된 생성자도 실행되도록 설정
return class extends constructor {
first_prop = 'override'; // 데코레이터에서 새로 프로퍼티를 덮어씌움
new_prop = 'new property'; // 데코레이터에서 새로 프로퍼티를 추가
};
}
@classDecorator
class Test {
first_prop: string;
constructor(m: string) {
this.first_prop = m;
}
}
let t = new Test('abcdefg');
console.log(t);
console.log( t.first_prop ); // 'override'
// 데코레이터로 설정된 프로토타입 확장은 타입 단언(Type Assertion) 필요
console.log( (t as any).new_prop ); 'new property'
[Running] ts-node "index.ts"
Test {
first_prop: 'override',
new_prop: 'new property'
}
override
new property
: 생성자가 프로퍼티로 덮어씌워졌다
- 데코레이터 컨테이너 활용
// 데코레이터 컨테이너
function classDecorator(param1: string, param2: string) {
// 데코레이터 함수
return function <T extends { new (...args: any[]): {} }>(constructor: T) {
return class extends constructor {
new_prop = param1;
first_prop = param2;
};
};
}
@classDecorator('안녕하세요', '반갑습니다')
class Test {
first_prop: string;
constructor(m: string) {
this.first_prop = m;
}
}
let t = new Test('world');
console.log(t);
[Running] ts-node "index.ts"
Test {
first_prop: '반갑습니다',
new_prop: '안녕하세요'
}
: 데코레이터를 사용할때 파라미터를 전달해서 사용할 수 있다
메서드 데코레이터
: 메서드 선언 직전에 선언하는 데코레이터
: 메서드의 프로터티 설명자 (property descriptor)에 적용되며 메서드 정의를 관찰, 수정, 대체하는데 사용
: 선언 파일, 오버로드 또는 기타 주변 컨텍스트 (ex) 선언 클래스)에서 사용할 수 없음
- 매개변수
: static 프로퍼티라면 클래스의 생성자 함수, 인스턴스 프로퍼티면 클래스의 prototype 객체
: 해당 메서드의 이름
: 해당 메서드의 property descriptor
- descriptor 재정의
: 메서드 데코레이터는 호출된 메서드로 정의된 메서드 순으로 전달을 받는데, 해당 메서드가 호출될때 특정 로직을 동작시키고 싶으면 아래와 같이 descriptor를 재정의 할 수 있다
: value (현재 값의 value)
: writable (수정 가능하면 true)
: enumarable (순회가 가능하면 true)
: configurable (property definition이 수정 및 삭제가 가능하면 true)
- 예시
function enumerable(value: boolean) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
descriptor.enumerable = value;
};
}
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
@enumerable(false)
greet() {
return "Hello, " + this.greeting;
}
}
접근자 데코레이터
: 접근자 선언 바로 전에 선언
: 접근자의 프로퍼티 설명자에 적용되며 접근자의 정의를 관찰, 수정, 교체하는데 사용
: 선언 파일이나 주변 컨텍스트 (ex) 선언 클래스)에서 사용할 수 없음
- 예시
function configurable(value: boolean) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
descriptor.configurable = value;
};
}
class Point {
private _x: number;
private _y: number;
constructor(x: number, y: number) {
this._x = x;
this._y = y;
}
@configurable(false)
get x() { return this._x; }
@configurable(false)
get y() { return this._y; }
}
프로퍼티 데코레이터
: 프로퍼티 선언 바로 전에 선언되는 데코레이터
: 선언 파일이나 주변 컨텍스트 (ex) 선언 클래스)에서 사용할 수 없음
- 매개변수
: static 프로퍼티면 클래스의 생성자 함수, 인스턴스 프로퍼티라면 클래스의 property 객체
: 해당 property의 이름
- 반환 값
: Property Descriptor 형태
: void
- 예시
function writable(writable: boolean) {
return function (target: any, decoratedPropertyName: any): any {
return {
writable,
};
};
}
class Test {
property = 'property';
@writable(false)
public data1 = 0;
@writable(true)
public data2 = 0;
}
const t = new Test();
t.data1 = 1000;
t.data2 = 1000; // 런타임 에러 !! - data2는 writable이 false라서 값을 대입할 수가 없다.
매개변수 데코레이터
: 매개변수 선언 직전에 선언되는 데코레이터
: 클래스 생성자 또는 메서드 선언의 함수에 적용
: 선언 파일, 오버로드 또는 다른 주변 컨텍스트 (ex) 선언 클래스)에서 사용할 수 없다
- 매개변수
: static 프로퍼티라면 클래스의 생성자 함수, 인스턴스 프로퍼티라면 클래스의 prototype 객체
: 매개변수가 들어있는 method의 이름
: 메서드 파라미터 목록에서의 index
- 예시
function parameterDecorator(target: any, methodName: string, paramIndex: number) {
console.log('parameterDecorator start');
console.log(target);
console.log(methodName);
console.log(paramIndex);
console.log('parameterDecorator end');
}
class Test7 {
private _name: string;
private _age: number;
constructor(name: string, @parameterDecorator age: number) {
this._name = name;
this._age = age;
}
print(@parameterDecorator message: string) {
console.log(message);
}
}
[Running] ts-node "index.ts"
parameterDecorator start
Test7 { print: [Function] }
print
0
parameterDecorator end
parameterDecorator start
[Function: Test7]
undefined
1
parameterDecorator end
데코레이터의 호출순서
: 프로퍼티 > 메서드 > 파라미터 > 클래스
reference
'Programming > TypeScript' 카테고리의 다른 글
@types 가 존재하지 않을 때 해결 방법 (0) | 2024.01.20 |
---|---|
타입스크립트와 객체지향 프로그래밍 (OOP) (1) | 2024.01.13 |
프로젝트 내에서 타입 관리하기 (0) | 2023.12.28 |
타입스크립트로 구현하는 좋은 예외(Exception) 처리 (0) | 2023.12.28 |
타입스크립트 클린코드 (0) | 2023.12.28 |