[typescript] 타이프 스크립트 데코레이터를 구현하는 방법은 무엇입니까?

TypeScript 1.5 에는 이제 데코레이터가 있습니다.

누군가 데코레이터를 구현하는 올바른 방법을 보여주는 간단한 예제를 제공하고 가능한 데코레이터 서명의 인수가 무엇을 의미하는지 설명 할 수 있습니까?

declare type ClassDecorator = <TFunction extends Function>(target: TFunction) => TFunction | void;
declare type PropertyDecorator = (target: Object, propertyKey: string | symbol) => void;
declare type MethodDecorator = <T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>) => TypedPropertyDescriptor<T> | void;
declare type ParameterDecorator = (target: Function, propertyKey: string | symbol, parameterIndex: number) => void;

또한 데코레이터를 구현하는 동안 명심해야 할 모범 사례가 있습니까?



답변

나는 데코레이터와 놀았고 문서가 나오기 전에 이것을 활용하려는 사람을 위해 알아 낸 것을 문서화하기로 결정했습니다. 실수가 있으면 언제든지 수정하십시오.

일반 사항

  • 객체가 인스턴스화 될 때가 아니라 클래스가 선언 될 때 데코레이터가 호출됩니다.
  • 동일한 클래스 / 속성 / 방법 / 매개 변수에 여러 데코레이터를 정의 할 수 있습니다.
  • 생성자에는 데코레이터를 사용할 수 없습니다.

유효한 데코레이터는 다음과 같아야합니다.

  1. 데코레이터 유형 중 하나에 할당 할 수 있습니다 ( ClassDecorator | PropertyDecorator | MethodDecorator | ParameterDecorator).
  2. 데코 레이팅 된 값에 할당 할 수있는 값 (클래스 데코레이터 및 메서드 데코레이터의 경우)을 반환합니다.

참고


방법 / 공식 접근 자 데코레이터

구현 매개 변수 :

  • target: 클래스의 프로토 타입 ( Object).
  • propertyKey: 메소드 이름 ( string| symbol).
  • descriptor: A TypedPropertyDescriptor— 디스크립터의 키에 익숙하지 않다면, 이 문서 에서 Object.defineProperty세 번째 매개 변수 인 내용을 읽어 보는 것이 좋습니다 .

예-인수없는

사용하다:

class MyClass {
    @log
    myMethod(arg: string) {
        return "Message -- " + arg;
    }
}

이행:

function log(target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>) {
    const originalMethod = descriptor.value; // save a reference to the original method

    // NOTE: Do not use arrow syntax here. Use a function expression in 
    // order to use the correct value of `this` in this method (see notes below)
    descriptor.value = function(...args: any[]) {
        // pre
        console.log("The method args are: " + JSON.stringify(args));
        // run and store result
        const result = originalMethod.apply(this, args);
        // post
        console.log("The return value is: " + result);
        // return the result of the original method (or modify it before returning)
        return result;
    };

    return descriptor;
}

입력:

new MyClass().myMethod("testing");

산출:

메소드 인수는 다음과 같습니다. [ “testing”]

리턴 값은 다음과 같습니다. 메시지-테스트

노트:

  • 디스크립터 값을 설정할 때 화살표 구문을 사용하지 마십시오. 당신이 할 경우 의 문맥은 this인스턴스의 것이 아닙니다.
  • 새 설명자를 반환하여 현재 설명자를 덮어 쓰는 것보다 원래 설명자를 수정하는 것이 좋습니다. 이를 통해 다른 데코레이터가 한 일을 덮어 쓰지 않고 설명자를 편집하는 여러 데코레이터를 사용할 수 있습니다. 이렇게하는 것은 당신이 좋아하는 무언가를 사용할 수 @enumerable(false)@log같은 시간에 (예 : 나쁜좋은 )
  • 유용한 : 유형 인수 TypedPropertyDescriptor메소드 서명 (무엇을 제한 할 수 있습니다 방법 예 ) 또는 접근 서명 ( 접근 자 예 ) 데코레이터가 넣을 수 있습니다.

예-인수 사용 (데코레이터 팩토리)

인수를 사용할 때 데코레이터의 매개 변수를 사용하여 함수를 선언 한 다음 인수없이 예제의 서명이있는 함수를 반환해야합니다.

class MyClass {
    @enumerable(false)
    get prop() {
        return true;
    }
}

function enumerable(isEnumerable: boolean) {
    return (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>) => {
        descriptor.enumerable = isEnumerable;
        return descriptor;
    };
}

정적 메소드 데코레이터

몇 가지 차이점이있는 메소드 데코레이터와 유사합니다.

  • 그것의 target매개 변수는 생성자 함수 자체가 아닌 프로토 타입입니다.
  • 디스크립터는 프로토 타입이 아닌 생성자 함수에 정의되어 있습니다.

클래스 데코레이터

@isTestable
class MyClass {}

구현 매개 변수 :

  • target: 데코레이터가 선언 된 클래스 ( TFunction extends Function).

사용 예 : 메타 데이터 API를 사용하여 클래스에 정보 저장


부동산 데코레이터

class MyClass {
    @serialize
    name: string;
}

구현 매개 변수 :

  • target: 클래스의 프로토 타입 ( Object).
  • propertyKey: 속성 이름 ( string| symbol).

사용 예 : @serialize("serializedName")데코레이터를 만들고 직렬화 할 속성 목록에 속성 이름을 추가합니다.


파라미터 데코레이터

class MyClass {
    myMethod(@myDecorator myParameter: string) {}
}

구현 매개 변수 :

  • target: 클래스의 프로토 타입 ( Function– 그것은 보인다 Function더 이상 작동하지 않습니다 당신은 사용해야합니다. anyObject지금 여기있는 클래스 내에서 데코레이터를 사용하기 위해 또는 클래스 형을 (지정) 당신이 그것에을 제한합니다.)
  • propertyKey: 메소드 이름 ( string| symbol).
  • parameterIndex: 함수 매개 변수 목록에서 매개 변수의 색인 ( number).

간단한 예

자세한 예


답변

다른 답변에서 볼 수없는 중요한 점은 다음과 같습니다.

데코레이터 공장

데코레이터가 선언에 적용되는 방식을 사용자 정의하려면 데코레이터 팩토리를 작성할 수 있습니다. 데코레이터 팩토리는 단순히 데코레이터가 런타임에 호출 할 표현식을 반환하는 함수입니다.

// This is a factory, returns one of ClassDecorator,
// PropertyDecorator, MethodDecorator, ParameterDecorator
function Entity(discriminator: string):  {
    return function(target) {
        // this is the decorator, in this case ClassDecorator.
    }
}

@Entity("cust")
export class MyCustomer { ... }

TypeScript 핸드북 데코레이터 장을 확인하십시오 .


답변

class Foo {
  @consoleLogger
  Boo(name:string) { return "Hello, " + name }
}
  • 대상 : 위의 경우 클래스의 프로토 타입은 “Foo”입니다.
  • propertyKey : 위의 경우 “Boo”에서 호출 된 메소드의 이름
  • 설명자 : object =>의 설명은 value 속성을 포함하며, 이는 함수 자체입니다. function (name) {return ‘Hello’+ name; }

콘솔에 대한 각 호출을 기록하는 것을 구현할 수 있습니다.

function consoleLogger(target: Function, key:string, value:any)
{
  return value: (...args: any[]) =>
  {
     var a = args.map(a => JSON.stringify(a)).join();
     var result = value.value.apply(this, args);
     var r = JSON.stringify(result);

     console.log('called method' + key + ' with args ' + a + ' returned result ' + r);

     return result;
  }
}


답변