데코레이터는 타입스크립트 5.0에서 추가됐습니다. 데코레이터는 클래스의 기능을 증강하는 함수로 여러 함수에서 공통으로 수행되는 부분을 데코레이터로 만들어두면 좋습니다.
아래 코드는 클래스 메서드들에는 중복되는 start, end 로그가 있습니다.
class ANoDeco {
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<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) {
console.log(context.name, 'start');
const result = originalMethod.call(this, ...args);
console.log(context.name, 'end');
return result;
}
return replacementMethod;
}
class ADeco {
@startAndEnd
eat() {
console.log('eat');
}
@startAndEnd
work() {
console.log('work');
}
@startAndEnd
sleep() {
console.log('sleep');
}
}
const a = new ADeco();
a.eat();
결과
eat가 나오는 이유는 context.name에는 데코레이터를 장식하는 메서드의 이름이 있기 때문입니다.
eat start
eat
eat end
context의 종류에는 아래와 같이 다양하며
ClassDecoratorContext: 클래스 자체를 장식할 때
ClassMethodDecoratorContext: 클래스 메서드를 장식할 때
ClassGetterDecoratorContext: 클래스 getter를 장식할 때
ClassSetterDecoratorContext: 클래스 setter를 장식할 때
ClassMemberDecoratorContext: 클래스 멤버를 장식할 때
ClassAccessorDecoratorContext: 클래스 accessor를 장식할 때
ClassFieldDecoratorContext: 클래스 필드를 장식할 때
context 타입은 아래처럼 되어있습니다.
type Context = {
kind: string; // 데코레이터 유형 ClassDecoratorContext라면 class
name: string | symbol; // 장식 대상 이름
access: { // has, get, set 접근자
get?(): unknown;
set?(value: boolean): void;
has?(value: unknown): boolean;
};
private?: boolean; // private 여부
static?: boolean; // static 여부
addInitializer?(initializer: () => void): void; // 초기화 함수
}
데코레이터도 매개변수를 넣어줄 수 있습니다.
아래 코드처럼 고차 함수의 형태로 감싸주면 됩니다. 기존 데코레이터에서 원하는 파람을 넣을 수 있도록 래핑 해주면 됩니다.
function startAndEndPram(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) {
console.log(context.name, start);
const result = originalMethod.call(this, ...args);
console.log(context.name, end);
return result;
}
return replacementMethod;
};
}
그럼 해당 데코레이터를 적용시킨 후 실행시키면
class AParamDeco {
@startAndEndPram()
eat() {
console.log('eat');
}
@startAndEndPram()
work() {
console.log('work');
}
@startAndEndPram('안녕', '하세요')
sleep() {
console.log('sleep');
}
}
const a = new AParamDeco();
a.sleep();
아래처럼 결과가 나옵니다.
sleep 안녕
sleep
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);
this.log('클래스 인스턴스 생성!');
}
log(msg: string): void {
console.log(msg);
}
};
}
return value;
}
@log
class C {}
const c = new C();
'언어 > 타입스크립트' 카테고리의 다른 글
컨트롤 + S로 타입스크립트 파일 재실행하기 (0) | 2023.05.10 |
---|