what can we get out of it
What is Typescript Decorator
?
"A Decorator is a special kind of declaration that can be attached to a class declaration,
method, accessor, property, or parameter"
Decorator === Function
@NgModule({
imports: [
CommonModule,
],
exports: [
],
})
export class NbThemeModule {}
@Component({
selector: 'nb-card-header',
template: `<ng-content></ng-content>`,
})
export class NbCardHeaderComponent {}
@Entity()
export class Project {
@PrimaryGeneratedColumn()
id: number;
@Column('text')
name: string;
}
> tsconfig.json
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "es2017",
},
}
How we can use it
?
Practical Use Cases
Real Use Cases
class TestServiceDeco {
@LogTime()
function testLogging() {
...
}
}
@LogTime()
function testLogging() {
...
}
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;
};
};
}
"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;
decorator itself
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);
decorators call
Javascript uses
__decorate
function to call your decorators
class TestServiceDeco {
@LogResult()
@LogTime()
public testLogging() {...}
}
several decorators
__decorate([
log_time_decorator_1.LogResult(),
log_time_decorator_1.LogTime(),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", void 0)
], TestServiceDeco.prototype, "testLogging", null);
@CustomBehavior({
singleton: false,
tags: ['singleton', 'test', 'service'],
})
class TestServiceDeco {
constructor() {
console.log('TestServiceDeco ctor');
}
}
personal dependency injector
import 'reflect-metadata';
interface Metadata {
singleton?: boolean;
tags?: string[];
}
function CustomBehavior(metadata: Metadata) {
return function(ctor: Function) {
Reflect.defineMetadata('metadataKey', metadata, ctor);
}
}
decorator itself
TestServiceDeco = __decorate([
class_meta_decorator_1.CustomBehavior({
singleton: false,
tags: ['singleton', 'test', 'service'],
}),
__metadata("design:paramtypes", [])
], TestServiceDeco);
import 'reflect-metadata';
const diMap: 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 (!diMap.has(tType)) {
diMap.set(tType, new tType());
}
return diMap.get(tType) as T;
} else {
return new tType() as T;
}
}
usage
const instance1 = getInstance(TestServiceDeco);
class Person {
@Age(18, 60)
age: number;
}
decorator itself
import 'reflect-metadata';
function Age(from: number, to: number) {
return function (prototype: Object, propertyName: string) {
const metadata = {
propertyName: propertyName,
length: { from, to },
};
Reflect.defineMetadata(`validate_age_${propertyName}`,
metadata, prototype.constructor);
};
}
class Person {
}
__decorate([
age_decorator_1.Age(18, 60),
__metadata("design:type", Number)
], Person.prototype, "age", void 0);
function validate<T>(object: T) {
const properties = Object.getOwnPropertyNames(object);
properties.forEach(propertyName => {
let metadata = Reflect.getMetadata(
metaKey + propertyName, object.constructor);
if (metadata && metadata.length) {
const value = object[metadata.propertyName];
if (value < metadata.length.from
|| value > metadata.length.to) {
throw new Error('Validation failed');
}
}
});
}
usage
const person = new Person();
person.age = 40;
validate(person);
export class Post {
@Length(10, 20)
title: string;
@IsInt()
@Min(0)
@Max(10)
rating: number;
@IsEmail()
email: string;
}
npm install class-validator --save
const post = new Post();
post.title = 'Test';
validate(object)
.then(errors => {
...
});
@Typed()
public multiplyChecked(num: number, num2: number): number {
return num * num2;
}
npm install ts-stronger-types --save