programing

타이프 스크립트 데코레이터를 구현하는 방법

css3 2023. 3. 6. 21:23

타이프 스크립트 데코레이터를 구현하는 방법

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. 하나의 타입Decorator」)에(「Decorator」).ClassDecorator | PropertyDecorator | MethodDecorator | ParameterDecorator를 참조해 주세요.
  2. 장식된 값에 할당할 수 있는 값(클래스 장식자 및 메서드 장식자의 경우)을 반환합니다.

언급


메서드/포멀액세서 데코레이터

구현 파라미터:

  • target: 클래스의 프로토타입(Object를 참조해 주세요.
  • propertyKey : 서서 : : : : : : ) 。string| symbol).
  • descriptor: A - 디스크립터의 키가 익숙하지 않은 경우는, 다음의 메뉴얼을 참조해 주세요.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");

출력:

메서드 arg는 ["testing"]입니다.

반환값은 Message --testing 입니다.

주의:

  • 설명자 값을 설정할 때 화살표 구문을 사용하지 마십시오.이 경우 의 컨텍스트는 인스턴스의 컨텍스트가 아닙니다.
  • 새 설명자를 반환하여 현재 설명자를 덮어쓰는 것보다 원래 설명자를 수정하는 것이 좋습니다.이렇게 하면 다른 데코레이터가 한 작업을 덮어쓰지 않고 설명자를 편집하는 여러 데코레이터를 사용할 수 있습니다. '어울리다' 같은 것을 할 수 요.@enumerable(false) ★★★★★★★★★★★★★★★★★」@log동시에 (: Bad vs Good)
  • [유용]: 의 type 인수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더 이상 작동하지 않습니다. 하면 됩니다.any ★★★★★★★★★★★★★★★★★」Object어떤 클래스에서도 데코레이터를 사용할 수 있게 되었습니다. 제한할
  • 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")
  • 설명자: 개체 =>에 대한 설명에 값 속성이 포함되어 있습니다. 이 속성은 함수 자체입니다. 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;
  }     
}

TS 데코레이터:

TS 데코레이터를 사용하면 클래스에 추가 기능을 추가할 수 있습니다.클래스는 클래스 인스턴스가 생성되기 전에 선언 시 데코레이터에 의해 변경됩니다.

구문:

데코레이터는 다음과 같이 선언됩니다.@예를 들어 부호@metadata. TS는 대응하는 메타데이터 함수를 검색하여 정확하게 장식된 항목에 따라 다른 sevaral 인수를 자동으로 제공합니다(예를 들어 클래스 또는 클래스 속성에서 다른 인수를 얻습니다).

다음 파라미터는 데코레이터 함수로 제공됩니다.

  • 클래스의 원형 개체
  • 속성 키 또는 메서드 이름
  • PropertyDescriptor 개체는 다음과 같습니다.{writable: true, enumerable: false, configurable: true, value: ƒ}

이들 인수 중 1~3은 데코레이터의 종류에 따라 데코레이터 기능에 전달됩니다.

데코레이터 유형:

다음 데코레이터를 클래스에 적용할 수 있으며 TS는 다음 순서로 데코레이터를 평가합니다(다음 합계를 TS 문서에서 참조).

  1. 각 인스턴스 구성원에 대해 매개 변수 장식기 다음에 메서드, 접근자 또는 특성 장식기가 적용됩니다.
  2. 각 정적 구성원에 대해 Method, Accessor 또는 Property Decorator에 이어 Parameter Decorator가 적용됩니다.
  3. 생성자에 매개변수 장식기가 적용됩니다.
  4. 클래스에 클래스 데코레이터가 적용됨

그것들을 더 잘 이해하는 가장 좋은 방법은 예를 들어보는 것이다.이러한 예에서는 TS 언어와 다음과 같은 개념을 충분히 이해할 필요가 있습니다.PropertyDescriptor.

메서드 데코레이터:

function overwrite(
    target: myClass,
    propertyKey: string,
    descriptor: PropertyDescriptor
) {
    console.log('I get logged when the class is declared!')

    // desciptor.value refers to the actual function fo the class
    // we are changing it to another function which straight up 
    // overrides the other function
    descriptor.value = function () {
        return 'newValue method overwritten'
    }
}

function enhance(
    target: myClass,
    propertyKey: string,
    descriptor: PropertyDescriptor
) {
    const oldFunc = descriptor.value;

    // desciptor.value refers to the actual function fo the class
    // we are changing it to another function which calls the old
    // function and does some extra stuff
    descriptor.value = function (...args: any[]) {
        console.log('log before');
        const returnValue = oldFunc.apply(this, args)
        console.log('log after');

        return returnValue;
    }
}


class myClass {

    // here is the decorator applied
    @overwrite
    foo() {
        return 'oldValue';
    }

    // here is the decorator applied
    @enhance
    bar() {
        return 'oldValueBar';
    }

}

const instance =new myClass()

console.log(instance.foo())
console.log(instance.bar())

// The following gets logged in this order:

//I get logged when the class is declared!
// newValue method overwritten
// log before
// log after
// oldValueBar

속성 장식자:

function metaData(
    target: myClass,
    propertyKey: string,
    // A Property Descriptor is not provided as an argument to a property decorator due to 
    // how property decorators are initialized in TypeScript.
) {

    console.log('Execute your custom code here')
    console.log(propertyKey)

}

class myClass {

    @metaData
    foo = 5

}


// The following gets logged in this order:

// Execute your custom code here
// foo

클래스 데코레이터(TS 문서):

function seal(
    constructor: Function,
) {

    // Object.seal() does the following:
    // Prevents the modification of attributes of 
    // existing properties, and prevents the addition 
    // of new properties
    Object.seal(constructor);
    Object.seal(constructor.prototype);

}

@seal
class myClass {

    bar?: any;
    
    foo = 5

}
 
myClass.prototype.bar = 10;

// The following error will be thrown:

// Uncaught TypeError: Cannot add property bar,
// object is not extensible
 

데코레이터 및 데코레이터 공장:

데코레이터는 데코레이터 기능 또는 데코레이터 공장 기능을 통해 선언할 수 있습니다.구문에는 다음과 같은 차이가 있습니다.이러한 구문은 예를 통해 가장 잘 설명하겠습니다.

// Returns a decorator function, we can return any function
// based on argument if we want
function decoratorFactory(arg: string) {
    return function decorator(
    target: myClass,
    propertyKey: string,
) {
    console.log(`Log arg ${arg} in decorator factory`);
}
}

// Define a decorator function directly
function decorator(
    target: myClass,
    propertyKey: string,
) {
    console.log('Standard argument');
}

class myClass {

    // Note the parentheses and optional arguments 
    // in the decorator factory
    @decoratorFactory('myArgument')
    foo = 'foo';

    // No parentheses or arguments
    @decorator
    bar = 'bar';

}


// The following gets logged in this order:

// Log arg myArgument in decorator factory
// Standard argument

또,decorate/enhance원본 생성자에 대한 새로운 기능(전 3.9.7 사용)을 제공합니다.다음 스니펫은 원래 생성자를 랩하여 이름 속성의 접두사를 추가합니다.이 과정은 수업이 다음 시간에 따라instantiated 클래스가 '''일 때''입니다.declared!

 //Decorator function
 function Prefixer(prefix: string) { 
    return function<T extends { new (...args: any[]): {name: string} }>(
      originalCtor: T
    ) {
      return class extends originalCtor {
        constructor(..._: any[]) {
          super();
          this.name = `${prefix}.${this.name.toUpperCase()}`;        
          console.log(this.name);       
        }
      };
    };
  }

가 「」인 .instantiated은 원래 인 ctor 로직으로 됩니다.

  @Prefixer('Mr')
  class Person {
    name = 'MBB';
  
    constructor() {
      console.log('original ctr logic here!');
    }
  }
  
  const pers = new Person();
  
  console.log(pers); //Mr.MBB

언급URL : https://stackoverflow.com/questions/29775830/how-to-implement-a-typescript-decorator