TypeScript "데코레이터"및 그 사용 패턴 분석

이 강의에서는 TypeScript의 Decorator 패턴과 Decorator를 사용하여 클래스의 동작을 변경하는 방법에 대해 알아 봅니다. 또한이 reflect-metadata패키지가 데코레이터를 쉽게 디자인하는 데 어떻게 도움이 되는지 살펴볼 것 입니다.

(출처 : unsplash.com)

데코레이터는 클래스 선언 또는 클래스 멤버의 맨 위에 배치하는 주석 이며 해당 클래스 또는 필드의 작동 방식을 변경합니다. 당신이 경우 각도 개발자, 당신은 아마도에 대해 알고 @Component각도 구성 요소를 정의 장식.

(출처 : angular.io)

위의 예에서 @Component주석은 AppComponent클래스 를 장식하는 데코레이터입니다 . 기본적으로 데코레이터 주석과 함께 제공되는 구성을 사용하여이 클래스를 Angular 구성 요소로 변환합니다. 마찬가지로 클래스 인스턴스 필드@Input주석은 데코레이터입니다.

우리는 이전 강의 에서 자바 스크립트에서의 메타 프로그래밍 과 그 사용 사례에 대해 이야기 했습니다 . 간단히 말해서 메타 프로그래밍은 프로그램의 동작을 검사하고 제어하는 ​​프로그래밍 패턴입니다. 예를 들어 위 의 데코레이터는 클래스 의 동작을 변경합니다 .@ComponentAppComponent

Decorator는 JavaScript 함수에 불과하지만 @접두사 가있는 클래스 또는 멤버의 맨 위에 배치 되면 런타임에 특정 인수와 함께 호출됩니다. 이러한 인수는 장식중인 클래스 또는 멤버의 내부를 나타냅니다. 이 함수 내에서 프로그램의 동작을 변경하는 이러한 내부를 변경할 수 있습니다.

데코레이터는 열거 형이나 인터페이스와 달리 TypeScript 기능이 아닙니다. 기본 JavaScript 기능이지만 아직 표준화되어 있어야합니다. 이 제안은 ECMAScript 제안 트랙의 2 단계 입니다. 그러나 Babel 플러그인을 사용하여 JavaScript에서 데코레이터를 구현할 수 있습니다 . 우리는 Babel CLI를 사용하여 데코레이터를 트랜스 파일하는 방법을 보여준“ 자바 스크립트 데코레이터에 대한 최소 가이드 ”강의에서 데코레이터 를 자세히 살펴 보았습니다 .

데코레이터는 현재 ECMAScript 제안의 일부가 아니며 ECMAScript 제안 트랙의 2 단계에 있으므로 활발하게 개발 중입니다. 최근에 상당한 변화가 있었기 때문에 데코레이터에 대한 위의 기사를 수정해야했습니다 ( 이전 버전 여기 ).

TypeScript는 제안이 초기 단계에있을 때 데코레이터를 구현했습니다. 즉, 제안이 ( 현재 ) 지정 하고 TypeScript가 구현하는 것이 더 이상 일치하지 않음을 의미합니다. TypeScript가 현재 데코레이터 제안 의 레거시 버전 이라고 부르는 것을 구현하지만 데코레이터는 매우 흥미롭고 매우 유용합니다.

💡 TypeScript 현재 데코레이터 제안의 변경 사항을 구현 하기열망하지 않습니다 . 이러한 변경 사항은 응용 프로그램에 주요 변경 사항을 도입하고 신은 얼마나 많은 타사 패키지가 영향을 받을지 알고 있기 때문에 제안이 안정되거나 ECMAScript 표준에 포함될 준비가 될 때까지 기다리는 것이 모든 사람에게 가장 유익하다고 생각합니다.

데코레이터는 ECMAScript 표준의 일부가 아니며 현재 실험적인 것으로 간주되기 때문에 TypeScript는 명시 적으로 책임을지지 않고 데코레이터를 사용하도록 허용하지 않습니다. 따라서 파일 에서 experimentalDecoratorcompiler-option을 로 설정 하거나 명령에 compiler-flag를 제공해야 합니다.truetsconfig.json--experimentalDecorator

{
    "files": [
        "program.ts"
    ],
    "compilerOptions": {
        "target": "ES6",
        "experimentalDecorators": true,
        "removeComments": true,
        "alwaysStrict": true
    }
}

데코레이터는 클래스 또는 해당 멤버 ( 예 : 속성, 메서드, 접근 자 등 ) 만 데코 레이팅 할 수 있습니다 . TypeScript는 클래스 선언 , 메서드 , 접근 자 ( getter / setter 메서드 ), 메서드 매개 변수 ( 생성자 포함 ) 및 클래스 속성에 대한 데코레이터를 지원 합니다 .

클래스 선언 데코레이터

@Component앞에서 본 데코레이터는 클래스 선언 데코레이터의 한 유형이거나 단순히 클래스 자체를 수정하는 클래스 데코레이터 입니다. 데코레이터는 단순한 JavaScript 함수이며 다른 것은 없습니다. 이제 클래스와 프로토 타입을 고정하는 간단한 데코레이터를 만들어 보겠습니다.

(decorators / class-decorator.ts)

위의 프로그램에서 우리는 일부 정적 속성, 일부 인스턴스 속성 및 일부 인스턴스 메서드 Person가있는 간단한 JavaScript ( ES6 ) 클래스 인 클래스 를 선언 했으므로 여기에서는 특별한 것이 없습니다. 그러나 우리는 @lock데코레이터 로이 클래스를 장식했습니다.

데코레이터가 진행함에 따라는 lock런타임에 JavaScript 엔진에 의해 호출되는 JavaScript 함수입니다. 때 lock함수가 실행되고, 그것은 클래스의 호출되는 생성자 함수 이 장식 클래스를 장식입니다으로 만 인수로.

이 인수는 우리가 클래스를 장식하고 있고받은 인수가 생성자 함수이기 때문에 일부 사람들을 혼란스럽게합니다. 무슨 일이야? 글쎄요, classES6에서 수업을 만들기 위해받은 멋진 키워드입니다. 그러나 내부적으로 ( JavaScript 엔진에서 ) 생성자 함수와 프로토 타입으로 나뉩니다 . 위 프로그램은 아래 프로그램과 유사합니다.

function Person(fname, lname) {
    this.fname = fname;
    this.lname = lname;
}
Person.version = 'v1.0.0';
Person.prototype.getFullName = function() {
    return this.fname + ' ' + this.lname;
}

에서 lock장식, 사용 방법, 따라서 우리는 우리가 코멘트에 나타난 오류에서 분명 런타임에 그들에 어떤 속성을 추가하거나 수정할 수 없습니다, 생성자 함수와 프로토 타입을 모두 동결.Object.freeze()

@JavaScript에서 접두사가 지원되지 않는 경우 이것이 어떻게 작동 하는지 궁금 할 것 입니다. 음, 프로그램을 컴파일 할 때 TypeScript 컴파일러는 @데코레이터 이름과 함께 접두사를 제거 하고 데코레이터 함수를 실행 하는 도우미 함수 호출로 대체합니다 .

위 프로그램은 command를 class-decorator.ts이용하여 컴파일 한 파일 의 컴파일 된 코드입니다 tsc. 여기 에서 클래스 와 함께 데코레이터 함수 __decorate를 호출하는 도우미 함수입니다 .lockPerson

현재 lock데코레이터 함수는 값을 반환하지 않습니다. 클래스 데코레이터가 값을 반환하는 경우 원래 생성자 함수 ( 또는 클래스 )를 대체 할 생성자 함수 ( 또는 클래스 ) 여야합니다 . 여기서는 원래 프로토 타입을 유지 관리 할 책임이 있습니다.

function decorator(class) {
  // modify 'class' or return a new one
}
(decorators/class-decorator-extend.ts)

💡 강의 에서 일반 유형일반 제약 조건 에 대해 논의했습니다 . 클래스의 정적 측면 유형에 대해 자세히 알고 싶다면 단원을 따르십시오 .

withInfo데코레이터 함수 내에서 클래스를 확장하는 새 클래스를 반환합니다 Person. 이 새 클래스에는 생성자가 암시 적으로 호출 constructor된다는 의미 가 없습니다 Person. person객체와 프로토 타입 체인 보이는 아래 좋아합니다.

(Chrome DevTools)

메소드 데코레이터

메서드 데코레이터는 클래스의 정적 또는 인스턴스 메서드를 장식합니다 ( 제외constructor ). 이 경우 데코레이터 함수는 세 개의 인수를받습니다. 첫 번째 인수는 target메서드가 속한 것입니다. 메서드가 정적 인 경우 생성자 함수 ( class ) 이거나 메서드가 인스턴스 메서드 인 경우 클래스 의 생성자 함수일 수 있습니다 .prototype

function decorator(target, name, descriptor) {
  // modify 'descriptor' or return new one
}
(decorators / method-decorator.ts)

위의 예에서 readonly데코레이터 함수 writable는 속성 설명 자의 설정 만 수정 하지만 prefix데코레이터 함수는 새 속성 설명자를 반환합니다. value방법의 속성 디스크립터 필드는 구현 방법 (포함 된 기능 ).

💡 컴파일 대상이로 설정 ES3되면 메서드 데코레이터 함수는 속성 설명자인 세 번째 인수를받지 않습니다. 또한 반환 값 (속성 설명자)도 무시됩니다. 이는 ES3의 속성 설명자가 제대로 지원되지 않기 때문입니다.

접근 자 데코레이터

접근 자 데코레이터와 메서드 데코레이터 사이에는 사실상 차이가 없습니다. 정적 또는 인스턴스 메서드에 get또는 set접두사가 있으면 접근 자라고합니다. 메서드에 get접두어 ( getter 메서드 )가있는 경우 속성 설명자에는 getfield 대신 메서드 정의를 포함하는 필드가 있습니다 value. 마찬가지로 setter 메서드는 set필드에 저장됩니다 .

class Person {
    constructor(
        public fname: string, public lname: string
    ) {}
get fullname(): string {
        return this.fname + ' ' + this.lname;
    }
set fullname( name ) {
        [ this.fname, this.lname ] = name.split(' ');
    }
}

따라서 이러한 접근 자에 대해 동일하거나 다른 데코레이터를 개별적으로 제공하는 것이 공정 해 보이지만 TypeScript에서는 이러한 관행을 권장하지 않습니다. 아래 예제에서 getter와 같은 첫 번째 접근 자에 추가되어야하는 단일 장식자를 사용하여 두 접근자를 장식 할 수 있습니다.

(decorators / accessor-decorator.ts)

위의 예에서 uppercase데코레이터 함수를 사용하여 fullname접근 자 메서드에 대한 새 속성 설명자를 반환했습니다 . 이 속성 설명자는 getset필드를 포함 합니다.

부동산 데코레이터

클래스 정적 및 인스턴스 속성을 장식 할 수도 있습니다. 이러한 속성에 대한 데코레이터 함수는 두 개의 인수 만받습니다. 첫 번째 인수는 target속성이 인 경우 생성자 함수가 될 수 있고 static속성이 인스턴스 속성 인 경우 클래스 프로토 타입이 될 수 있습니다. 두 번째 인수는 속성의 이름입니다.

function decorator(target, name) {
  // collect or store some information
}

따라서 속성 데코레이터 함수는 TypeScript의 속성 설명자와 함께 호출되지 않습니다. 또한 데코레이터 함수의 반환 값은 무시됩니다. 그렇다면 속성 설명자가 필요한 이유는 무엇입니까? 글쎄, 그것은 재산에 대한 정보를 캡처하고 나중에 그것을 사용하는 데 사용될 수 있습니다.

"reflect-metadata"패키지 소개 및 ECMAScript 제안

위의 강의 에서 리플 렉트 메타 데이터 제안과 사용할 수있는 위치에 대해 이야기 했습니다. reflect-metadata패키지는이 제안에 대한 polyfill입니다. 앞으로 나아 가기 전에이 강의를 읽어 보는 것이 좋습니다. 그렇지 않으면 이해하지 못할 것입니다. 그러니 먼저 읽어주세요.

Reflect.defineMetadata(key, value, target, prop)메서드는 개체 또는의 속성에 대한 value고유 키 key를 사용 하여 메타 데이터 값 을 정의 합니다 . 이 방법을 사용하여 나중에이 메타 데이터를 추출 할 수 있습니다.targetproptargetReflect.getMetadata

(decorators / property-decorator.ts)

위의 예에서 textCase데코레이터 함수는 키 와 함께 Reflect.defineMetadata데코레이터 target및 속성에 대한 메서드를 사용하여 메타 데이터 값을 설정합니다 . 이 메타 데이터 값은 해당 속성의 형식 지정 사례를 알려주 는 단순 합니다. 그래서이 로 변환되어야 하고 에 .namecasestringfnameuppercaselnamelowercase

나중에 fullname메서드를 사용하여 getter 메서드 에서이 메타 데이터를 추출합니다 Reflect.getMetdata. 이후 this인스턴스 메소드 (내부 또는 접근 인스턴스에) 포인트 this는 AS 같지 target우리 (장식 자 기능에 사용 하였다Person.prototype 메타 데이터를 저장하는).

하지만 이후 방법은 또한의 프로토 타입에 검색 의 프로토 타입이 될 것입니다 인수, 이다 , 메타 데이터 값을 찾을 수 있었다. 그래도 방법 은 그렇지 않습니다 .Reflect.getMetadata(key, target, prop)targetthisPerson.prototypeReflect.getOwnMetadata()

@Reflect.metadata(metadataKey, metadataValue)

이 장식은 주어진 몇 가지 메타 데이터를 저장하는 데 사용되는 등 클래스, 메소드, 접근, 장식하는 데 사용할 수 있도록이 메소드에 의해 반환되는 장식 기능은 모든 작동 target또는 property의를 target. 내부적으로, 반환 된 장식 기능은 호출 Reflect.defineMetada와 방법 metadataKey, metadataValuetarget및 / 또는 property.

(decorators / property-decorator-reflect.ts)

Reflect.metadata방법은 textCase데코레이터 기능 과 동일하게 작동 합니다. 는 Reflect.metadata메타 키를 받아 들여 메타 데이터 값에 대한 메타 데이터가 적용되는 장식 함수 반환 target( 시 클래스 ) 또는 상 property( 기타 가 장식된다).

매개 변수 데코레이터

파라미터 장식은 장식 있어서 파라미터 의 파라미터를 포함 constructor. 이러한 속성에 대한 데코레이터 함수는 세 개의 인수를받습니다. 첫 번째 target는 메서드가있는 경우 생성자 함수 static이거나 메서드가 인스턴스 메서드 인 경우 클래스 프로토 타입이 될 수있는 것입니다.

두 번째 인수는 메서드의 이름이고 세 번째 인수는 메서드 정의에있는 매개 변수의 서수 색인입니다.

function decorator(target, name, index) {
  // collect or store some information
}
(decorators / parameter-decorator.ts)

위의 예에서 textCase데코레이터 함수는 constructor. name생성자 함수 의 메서드 이름 undefined은 데코레이터 함수에서와 같이 target제공되며 프로토 타입이 아닌 클래스에 속하므로 클래스 자체로 제공됩니다.

index인수를 사용하여 각 매개 변수에 대한 메타 데이터 값을 저장합니다. 메타 데이터 값은 매개 변수의 인덱스로 식별 되므로 getter 메소드 에서 해당 메타 데이터를 추출하기 위해 메소드 이름으로 01값을 사용 undefined했습니다 fullname.

데코레이터에서 더 잘하기

데코레이터 팩토리

데코레이터 팩토리는 데코레이터를 반환하는 함수입니다. 기술적으로 Reflect.metadata우리가 이전에 사용한 방법 은 실제 데코레이터 함수를 반환하는 데코레이터 팩토리 입니다. 데코레이터 팩토리로 무언가를 꾸미고 싶을 때, 우리는 .@decoFactory(...args)

(decorators / class-decorator-factory.ts)

위의 예에서 볼 수 있듯이 version함수는 데코레이터 팩토리입니다. 따라서 @version(...)클래스를 장식하는 동안 호출 서명 으로 주석을 달아야합니다 .

데코레이터 팩토리는 데코레이터 를 무언가에 적용하기 전에 사용자 정의하는 데 유용합니다 . 데코레이터 팩토리는 사용자 정의 기능을 제공하고 재사용 할 수 있기 때문에 데코레이터 기능보다 많이 사용되는 것을 볼 수 있습니다.

데코레이터 체인

여러 데코레이터를 함께 연결할 수 있습니다. 단일 엔티티에 두 개 이상의 데코레이터를 연결하는 두 가지 방법이 있습니다. 아래 그림과 같이 서로 위에 놓을 수 있습니다. 이 경우 데코레이터는 위에서 아래평가 되지만 아래에서 위로 적용됩니다 .

@decoratorA
@decoratorB
entity

@decoratorA @decoratorB entity
(decorators / decorator-chaining.ts)

위의 결과에서 알 수 있듯이 팩토리 함수는 데코레이터가 연결된 순서로 호출되었지만 실제 데코레이터는 역순으로 적용되었습니다. name매개 변수 장식 에서했던 것처럼 데코레이터 팩토리를 데코레이터 기능과 혼합 할 수도 있습니다 .

장식 순서

위의 결과에서 클래스 데코레이터가 매개 변수 데코레이터보다 먼저 평가되었지만 매개 변수 데코레이터가 먼저 적용되었음을 분명히 알 수 있습니다. 서로 다른 종류의 데코레이터가 어떻게 평가되고 적용되는지 살펴 보겠습니다.

(decorators / decoration-order.ts)

위의 예에서 factory데코레이터 팩토리는 decoratorFactory이전 예에서 사용 된 것과 동일합니다 . 이 예에서는 우리가 장식 할 수있는 거의 모든 것을 장식했습니다. 결과에서 장식 순서를 볼 수 있습니다.

  1. 첫째, 인스턴스 속성은 인스턴스 메서드 매개 변수, 인스턴스 메서드 및 마지막으로 인스턴스 접근자가 따라 장식됩니다.
  2. 그런 다음 정적 속성은 정적 메서드 매개 변수, 정적 메서드 및 마지막으로 정적 접근자가 따라 장식됩니다.
  3. 그런 다음 생성자 매개 변수가 장식됩니다.
  4. 결국 수업이 장식됩니다.

TypeScript는 특정 메타 데이터를 클래스 또는 해당 멤버에 암시 적으로 추가 emitDecoratorMetadata하는 --emitDecoratorMetadata컴파일러 옵션 (또는 컴파일러 플래그)을 제공합니다 . 이 옵션을로 설정 true하면 TypeScript 컴파일러는 데코레이터를 사용하는 동안 컴파일 된 JavaScript에 추가 코드를 삽입합니다.

{
    "files": [
        "program.ts"
    ],
    "compilerOptions": {
        "target": "ES6",
        "experimentalDecorators": true,
        "emitDecoratorMetadata": true,
        "removeComments": true,
        "alwaysStrict": true
    }
}
(decorators/emit-decorator-metadata.ts)

이 메타 데이터가 설정된 위치를 이해하려면이 메타 데이터를 추가하는 로직이 TypeScript 컴파일러에 의해 컴파일 중에 추가되기 때문에 컴파일 된 JavaScript 코드를 확인해야합니다.

위의 코드 스 니펫에서 볼 수 있듯이 TypeScript 컴파일러는 데코레이터 팩토리 일 뿐인 도움말 함수를 사용하여 클래스 뿐만 아니라 메서드에 데코레이터 ( 와 함께noop )를 몇 개 더 자동으로 적용했습니다 .getNameWithPrefixPerson__metadata

이 데코레이터 팩토리는 Reflect.metdata메소드 의 도움으로 주어진 타겟 또는 속성에 대한 메타 데이터를 저장하는 데코레이터를 반환합니다 . 이러한 데코레이터는 데코레이터 주석이있는 클래스 또는 클래스 멤버에만 적용됩니다. 이러한 데코레이터의 역할은 다음과 같습니다.

  • 이 데코레이터는 키로 데코 레이팅하는 엔티티 의 런타임 데이터 유형 을 저장합니다 design:type.
  • 이 데코레이터는 design:paramtypes키로 데코 레이팅하는 메소드 인수의 런타임 데이터 유형을 저장합니다 .
  • 이 데코레이터는 design:returntype키로 데코 레이팅하고있는 메소드의 반환 값의 런타임 데이터 유형을 저장합니다 .

주의 사항

우리가 배운 것처럼 데코레이터는 클래스 또는 그 멤버의 동작을 변형하는 데 사용됩니다. TypeScript가이를 달성하는 방법은 런타임에 클래스를 수정하는 도우미 함수를 사용하는 것입니다.

클래스가 앰비언트 ( 키워드 사용 ) 또는 유형 선언 파일 내부로 선언 된 경우 이는 의미가 없을뿐만 아니라 앰비언트 선언 또는 유형 선언이 출력을 생성하지 않기 때문에 유효하지 않습니다.declare

또한 Person as any일부 프로그램에서 유형 어설 션을 사용했음을 알 수 있습니다. 예를 들어, 클래스 데코레이터 팩토리 예제를 사용하면 version데코레이터는 클래스 version에 정적 속성을 추가합니다 Person. 그러나에서 version속성에 액세스 Person하면 TypeScript 컴파일러가 불평합니다.

Property 'version' does not exist on type 'typeof Person'.
console.log( 'version ->', Person.version );
                                  ~~~~~~~
(thatisuday.com / GitHub / Twitter / StackOverflow / Instagram)

Suggested posts

좋은 습관을 만들고 유지하는 방법

장기적인 습관을 유지하기위한 생산성 팁

좋은 습관을 만들고 유지하는 방법

두 아이의 엄마가 된 후 일을 끝내기가 더 어려워졌습니다. 나는 한 작업에서 다음 작업으로 뛰어 들었고 하루가 끝날 때까지 아무것도하지 않았을 때 실망했습니다.

양자 컴퓨팅 Pt를위한 프로그래밍. 1 : NumPy

이제 양자 컴퓨팅이면의 물리학을 배우기 시작 했으므로 "이봐, 실제로 양자 컴퓨터를 사용하려면 실제로 양자 장치를 손으로 만들어야합니까?"라고 궁금해 할 것입니다. 답은 양자 컴퓨터를 컴퓨터라고 부르는 이유가 있습니다. 프로그래밍이 가능합니다! 이 시리즈에서는 수학 개념을 시뮬레이션하고 양자 컴퓨터에서 실행하는 소프트웨어를 구축하는 방법을 배우게됩니다. 깨끗하고 현대적이며 라이브러리가 풍부한 언어이기 때문에 Python을 사용하여 코드를 작성할 것입니다.

Related posts

성능 최적화 된 A / B 테스트 솔루션

성능 최적화 된 A / B 테스트 솔루션

의제 : 소개 : TL;하지만 읽을 수 있습니다. A / B 테스트, CloudFront 및 Lamba @ edge에 대해 이미 알고있는 경우 AWS Lambda @ edge를 사용한 A / B 테스트로 직접 이동하십시오. A / B 테스트 란 무엇입니까? A / B 테스트는 웹 사이트의 두 가지 버전에 대한 사용자의 참여를 비교하는 데 초점을 맞춘 UX 연구 방법론입니다.

fp-ts (Typescript)에서 Option 및 둘 중 하나 사용

저는 함수형 프로그래밍을 좋아합니다. 몇 년 동안 실수를하거나 토끼 구멍을 뚫는 것으로부터 저를 몇 번 구해 주었기 때문입니다. 동일한 입력이 주어지면 출력이 항상 동일하다는 것을 알면 안심입니다.

Syncfusion Blazor 파일 업로드 구성 요소에서 이미지를 미리 보는 방법

Syncfusion Blazor 파일 업로드 구성 요소에서 이미지를 미리 보는 방법

Syncfusion Blazor 파일 업로드는 하나 이상의 파일, 이미지, 문서, 오디오, 비디오 및 기타 파일을 서버에 업로드하기위한 구성 요소입니다. 여러 파일 선택, 진행률 표시 줄, 자동 업로드, 끌어서 놓기, 폴더 (디렉터리) 업로드, 파일을 포함하는 다양한 기능을 갖춘 HTML5 업로드 구성 요소 (<input type =”file”>)의 확장 버전입니다. 검증 등.

6 React 개발자로서 후회

내가 일찍했으면하는 것

6 React 개발자로서 후회

React는 배울 수있는 훌륭한 도구입니다. 그것은 우리가 우리 자신의 방식으로 일을 할 수있게합니다.