TypeScript의 Decorators

June 12, 2022

Decorator

  • Class와 Class 멤버에 대한 설명이나 수정을 지원하는 기능을 제공할 때 사용
  • 런타임에 함수로 호출
  • 아직은 실험적인 기능
  • @expression 과 같은 형식으로 사용

    • 런타임에 expression 함수 호출
function sealed(target) {
    // 'target' 변수와 함께 무언가를 수행합니다.
}

Decorator Factories

데코레이터가 선언 시 적용되는 방식을 바꾸고 싶을 때 쓴다. 단순히 runtime에 데코레이터에 의해 호출될 표현식을 리턴하는 함수다.

function color(value: string) { // 데코레이터 팩토리
  return function (target) {
    // 데코레이터
    // do something with 'target' and 'value'...
  };
}

데코레이터 합성

수학의 합성함수 방식과 비슷

@f
@g
x

위처럼 코드가 있는 경우 f(g(x))

  • 위 함수부터 평가됨
  • 아래에서 위로 결과가 전달됨

예시

function first() {
  console.log("first(): factory evaluated");
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    console.log("first(): called");
  };
}
 
function second() {
  console.log("second(): factory evaluated");
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    console.log("second(): called");
  };
}
 
class ExampleClass {
  @first()
  @second()
  method() {}
}

실행 결과

first(): factory evaluated
second(): factory evaluated
second(): called
first(): called

Class 데코레이터

  • Class 선언 직전에 선언
  • Class의 생성자를 유일한 인수로 받는다
@sealed
class BugReport {
  type = "report";
  title: string;
 
  constructor(t: string) {
    this.title = t;
  }
}

function sealed(constructor: Function) {
  Object.seal(constructor);
  Object.seal(constructor.prototype);
}
  • 타입 시스템에 정해진 Class의 타입을 데코레이터를 통해 바꿀 수 없음
function reportableClassDecorator<T extends { new (...args: any[]): {} }>(constructor: T) {
  return class extends constructor {
    reportingURL = "http://www...";
  };
}
 
@reportableClassDecorator
class BugReport {
  type = "report";
  title: string;
 
  constructor(t: string) {
    this.title = t;
  }
}
 
const bug = new BugReport("Needs dark mode");
console.log(bug.title); // Prints "Needs dark mode"
console.log(bug.type); // Prints "report"
 
// 데코레이터는 타입스크립트로 정의된 Class의 타입을 바꾸지 않음
// 그래서 데코레이터에서 새로 정의된 프로퍼티인 `reportingURL`는 타입 시스템에서는 알지 못함
bug.reportingURL;
// Property 'reportingURL' does not exist on type 'BugReport'.

Method 데코레이터

  • Method 선언 직전에 선언
  • 인수로 오는 것들

    1. target: 생성자 (클래스의 prototype)
    2. prorityKey: 멤버의 key명
    3. descriptor: 해당 멤버에 대한 descriptor
class Greeter {
  greeting: string;
  constructor(message: string) {
    this.greeting = message;
  }
 
  @enumerable(false)
  greet() {
    return "Hello, " + this.greeting;
  }
}

function enumerable(value: boolean) { // 데코레이터 팩토리
  return function (
		target: any, 
		propertyKey: string, 
		descriptor: PropertyDescriptor
	) {
    descriptor.enumerable = value;
  };
}

Parameter 데코레이터

  • 매개변수 선언 직전에 선언
  • 인수로 오는 것들

    1. target: 생성자 (클래스의 prototype)
    2. prorityKey: 멤버의 key명
    3. descriptor: 해당 멤버의 순서
class BugReport {
  type = "report";
  title: string;
 
  constructor(t: string) {
    this.title = t;
  }
 
  @validate
  print(
		@first verbose: boolean,
		@second errorCode: number,
		@third createdAt: date
	) {
    if (verbose) {
      return this.title;
    } else {
      return `type: ${this.type}\ntitle: ${this.title}`;
    }
  }
}

Reference


Written by Rita Ahn - frontend / UX

© 2023, Built with Gatsby and Leonids theme.