TS v5.0 에서 데코레이터 함수가 정식으로 추가됨
@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');
}
}
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 객체 타입
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; // 초기화(인스턴스 생성)힐 때 실행되는 메서
}
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');
}
}