Cada desenvolvedor Angular viu decoradores no código do timecode. Eles são usados para descrever módulos, configurar injeção de dependência ou configurar um componente. Em outras palavras, os decoradores são usados para descrever informações adicionais, ou metadados, para uma estrutura ou compilador (no caso do Angular). Além disso, Angular é apenas um exemplo. Existem muitas outras bibliotecas que usam decoradores para simplificar e visualizar o código, como uma abordagem declarativa. Como desenvolvedor .NET, no passado, vejo muitas semelhanças entre decoradores TS e atributos .NET. Por fim, a crescente estrutura de aplicativos de back-end do NestJS (uma abstração sobre o Node) também é construída sobre o uso intenso de decoradores e uma abordagem declarativa. Como tudo funciona e como usar decoradores em seu código,para torná-lo mais conveniente e legível? Todos entendemos que, depois de compilar o código TS, obtemos o código Javascript. No qual não há conceito de decorador, como muitos outros recursos do Typcript. Portanto, para mim, a pergunta mais interessante é o que o decorador se transforma após a compilação. Na sequência desta edição, fiz um discurso na reunião em Minsk e quero compartilhar o artigo.

. , .
@NgModule({
imports: [
CommonModule,
],
exports: [
],
})
export class NbThemeModule {}
- for component declaration
@Component({
selector: 'nb-card-header',
template: `<ng-content></ng-content>`,
})
export class NbCardHeaderComponent {}
, , , .
, tsconfig.json
, emitDecoratorMetadata
experimentalDecorators
, .
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "es2017",
},
}
, — , , , get , . @expression
, @
. expression
. , , .
, — , , . - .
, — . , -, ( ). , , .
— . Typescript :
declare type MethodDecorator =
<T>(
target: Object,
propertyKey: string | symbol,
descriptor: TypedPropertyDescriptor<T>)
=> TypedPropertyDescriptor<T> | void;
, . :
:
interface TypedPropertyDescriptor<T> {
enumerable?: boolean;
configurable?: boolean;
writable?: boolean;
value?: T;
get?: () => T;
set?: (value: T) => void;
}
, , .
, - , . Javascript .
, - . — .
class TestServiceDeco {
@LogTime()
testLogging() {
...
}
}
, . Typescript , . , - , .
:
function LogTime() {
return (target: Object, propertyName: string, descriptor: TypedPropertyDescriptor<Function>) => {
const method = descriptor.value;
descriptor.value = function(...args) {
console.time(propertyName || 'LogTime');
const result = method.apply(this, args);
console.timeEnd(propertyName || 'LogTime');
return result;
};
};
}
, — , . — target, propertyName . .
— , . , , . .
Javascript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function LogTime() {
return (target, propertyName, descriptor) => {
const method = descriptor.value;
descriptor.value = function (...args) {
console.time(propertyName || 'LogTime');
const result = method.apply(this, args);
console.timeEnd(propertyName || 'LogTime');
return result;
};
};
}
exports.LogTime = LogTime;
, Typescript . :
Object.defineProperty(exports, "__esModule", { value: true });
const log_time_decorator_1 = require("../src/samples/log-time.decorator");
class TestServiceDeco {
testLogging() {
... }
}
__decorate([
log_time_decorator_1.LogTime(),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", void 0)
], TestServiceDeco.prototype, "testLogging", null);
__decorate
, .
, target
prototype
, .
, __decorate
, , . , . .
, . — . — . — Dependency Injection. . , :
@CustomBehavior({
singleton: false,
})
class TestServiceDeco {
constructor() {
console.log('TestServiceDeco ctor');
}
}
, Typescript:
declare type ClassDecorator =
<TFunction extends Function>(target: TFunction)
=> TFunction | void;
:
import 'reflect-metadata';
interface Metadata {
singleton?: boolean;
}
function CustomBehavior(metadata: Metadata) {
return function(ctor: Function) {
Reflect.defineMetadata('metadataKey', metadata, ctor);
}
}
. — singelton- . .
:
Reflect-metadata Typescript. , — . , , .
import 'reflect-metadata';
const instancesMap: Map<Object, Object> = new Map<Object, Object>();
function getInstance<T>(tType: new () => T): T {
let metadata = Reflect.getMetadata('metadataKey', tType) as Metadata;
if (metadata.singleton) {
if (!instancesMap.has(tType)) {
instancesMap.set(tType, new tType());
}
return instancesMap.get(tType) as T;
} else {
return new tType() as T;
}
}
getInstance
, , ,Reflect.getMetadata
, . any
, as Metadata
- , .
tType: new () => T
- - , Map
, , .
getInstance
, .
, . , Javascript .
. , , , . , Person
Age
, 18 60. :
class Person {
@Age(18, 60)
age: number;
}
:
declare type PropertyDecorator =
(target: Object, propertyKey: string | symbol) => void;
:
import 'reflect-metadata';
function Age(from: number, to: number) {
return function (object: Object, propertyName: string) {
const metadata = {
propertyName: propertyName,
range: { from, to },
};
Reflect.defineMetadata(`validationMetadata_${propertyName}`, metadata, object.constructor);
};
}
, . . , , , . , .
:
class Person {
...
}
__decorate([
age_decorator_1.Age(18, 60),
__metadata("design:type", Number)
], Person.prototype, "age", void 0);
, __decorate
, .
, — , . — , , .
, :
function validate<T>(object: T) {
const properties = Object.getOwnPropertyNames(object);
properties.forEach(propertyName => {
let metadata = Reflect.getMetadata(metaKey + propertyName, object.constructor);
if (metadata && metadata.range) {
const value = object[metadata.propertyName];
if (value < metadata.range.from || value > metadata.range.to) {
throw new Error('Validation failed');
}
}
});
}
, , . .
:
const person = new Person();
person.age = 40;
validate(person);
person.age = 16;
validate(person);
, , , .
, :
declare type ParameterDecorator = (target: Object, propertyKey: string | symbol, parameterIndex: number) => void;
Class-Validator
Class-Validator, . .
export class Post {
@Length(10, 20)
title: string;
@IsInt()
@Min(0)
@Max(10)
rating: number;
@IsEmail()
email: string;
}
...
validate(object).then(errors => {
if (errors.length > 0) {
console.log("validation failed. errors: ", errors);
} else {
console.log("validation succeed");
}
});
, . .
, NestJS @UsePipes(new ValidationPipe())
http .
Typescript , . , -. , , , , , , , … Angular NestJS. ( ).
, , , , , !
ps enquanto escrevia (ou melhor, traduzia seu próprio artigo), encontrei outro bem ali, no hub, o que revela bem o tema dos decoradores. Eu dou um link