데코레이터 함수
데코레이터 함수
TS v5.0 에서 데코레이터 함수가 정식으로 추가됨
클래스의 기능을 증강하는 함수로 여러 함수에서 공통으로 수행되는 부분을 데코레이터로 만들어두면 좋다.
데코레이터를 여러개 붙힐 수 도 있다.
클래스 데코레이터의 경우 export나 export default 앞이나 뒤에 데코레이터를 붙일 수 있다.
단 앞과 뒤 동시에 붙이는건 안됨
@Log export class C {
export @Log class C {
@Log
export class C {
// eat, work, sleep이 start와 end를 로깅하는 console.log가 중복됨
// 이렇게 중복이 있는 경우 데코레이터를 통해 중복을 제거
class A {
eat() {
console.log('start');
console.log('Eat');
console.log('end');
}
work() {
console.log('start');
console.log('Work');
console.log('end');
}
sleep() {
console.log('start');
console.log('Sleep');
console.log('end');}
}
데코레이터를 통해 중복을 제거
function startAndEnd(originMethod: any, context: any) {
function replacementMethod(this: any, ...args: any[]) {
console.log('start');
const result = originalMethod.call(this, ...args);
console.log('end');
return result;
}
return replacementMethod;
}
class A {
@startAndEnd
eat() {
console.log('Eat');
}
@startAndEnd
work() {
console.log('Work');
}
@startAndEnd
sleep() {
console.log('Sleep');
}
}
originalMethod
매개변수 eat, work, sleep 같은 기존 메서드이 메서드가 대체 메서드(replacementMethod)로 바뀐다고 생각
any 타이핑을 제거해보자.
function startAndEnd<This, Args extends any[], Return>(
originalMethod: (this: This, ...args: Args) => Return,
context: ClassMethodDecoratorContext<This, (this: This, ...args: Args) => Return>
) {
function replacementMethod(this: This, ...args: Args): Return {
console.log('start');
const result = originalMethod.call(this, ...args);
console.log('end');
return result;
}
return replacementMethod;
}
Context
데코레이터 정보를 갖고 있는 매개변수
startAndEnd 데코레이터의 경우 클래스의 메서드를 장식하고 있으므로 context는
ClassmethodDecoratorContext
가 된다.어떤 문법을 장식하냐에 따라 context의 타입을 교체하면 된다.
// context 객체 타입
type Context = {
kind: string; // 데코레이터 유형, ClassDeceoratorContext라면 class
name: string | symbol; // 장식 대상의 이름
access: { // has, get, set 등의 접근자를 모아둔 객체
get?(): unknown;
set?(value: unknown): void;
has?(value: unknown): boolean;
};
private?: boolean; // private 여부
static?: boolean; // static 여부
addInitializer?(initializer: () => void): void; // 초기화(인스턴스 생성)힐 때 실행되는 메서
}
Context의 종류
ClassDecoratorContext
: 클래스 자체를 장식할 때ClassMethodDecoratorContext
: 클래스 메서드를 장식할 때ClassGetterDecoratorContext
: 클래스의 getter를 장식할 때ClassSetterDecoratorContext
: 클래스의 setter를 장식할 때ClassMemberDecoratorContext
: 클래스의 멤버를 장식할 때ClassAccessorDecoratorContext
: 클래스 accessor를 장식할 때ClassFiledDecoratorContext
: 클래스 필드를 장식할 때
데코레이터도 함수이다.
함수이므로 매개변수를 가질 수 있다.
다만 고차함수를 활용해야 한다.
function startAndEnd(start = 'start', end = 'end') {
return function RealDecorator<This, Args extends any[] , Return>(
originalMethod: (this: This, ...args: Args) => Return,
context: ClassMethodDecoratorContext<This, (this: This, ...args: Args) => Return>
) {
function replacementMethod(this: This, ...args: Args): Return {
console.log(contenxt.name, start);
const result = originalMethod.call(this, ...args);
console.log(context.name, end);
return result;
}
return replacementMethod;
}
}
class A {
@sttartAndEnd('시작', '끝')
sleep() {
console.log('Sleep');
}
}
또 다른 예시
function log<Input extends new (...args: any[]) => any> (
value: Input,
context: ClassDecoratorContext
) {
if (context.kind === 'class') {
return class extends value {
constructor(...args: any[]) {
super(args);
}
log(msg: string): void {
console.log(msg);
}
};
return value
}
}
function bound(originalMethod: unknown, context: ClassMethodDecoratorContext<any>) {
const methodName = context.name;
if (context.kind === 'method') {
context.addInitializer(function () {
this[methodName] = this[methodName].bind(this);
})
}
}
@log
export class C {
@bound
@startAndEnd()
eat() {
console.log('Eat');
}
@bound @startAndEnd() work() {
conosle.log('Work');
}
}
Last updated