Typescript Decorators
Decorators
- 多个
Decorators
执行顺序
function Logger(logString: string) {
console.log('1. LOGGER FACTORY');
return function(constructor: Function) {
console.log(`4. Logger decorator ${logString}`);
console.log(constructor);
};
}
function WithTemplate(template: string, hookId: string) {
console.log('2. TEMPLATE FACTORY');
return function(constructor: any) {
console.log('3. WithTemplatedecorator');
const hookEl = document.getElementById(hookId);
const p = new constructor();
if (hookEl) {
hookEl.innerHTML = template;
hookEl.querySelector('h1')!.textContent = p.name;
}
}
}
@Logger('LOGGING')
@WithTemplate('<h1>My Person Object</h1>', 'app')
class Person {
name = 'Max';
constructor() {
console.log('Creating person object...');
}
}
const pers = new Person();
// [LOG]: "1. LOGGER FACTORY"
// [LOG]: "2. TEMPLATE FACTORY"
// [LOG]: "3. WithTemplatedecorator"
// [LOG]: "Creating person object..."
// [LOG]: "4. Logger decorator LOGGING"
// [LOG]: class Person {
// constructor() {
// this.name = 'Max';
// console.log('Creating person object...');
// }
// }
// [LOG]: "Creating person object..."
Decorators
作用在Property
Accessor
Method
Parameter
Class
function PropertyDecorator(target: any, name: string | symbol) {
console.log('-------------------Property decorator---------------------');
console.log('target', target); // target {}
console.log('propertyName', name); // name title
console.log('-------------------Property decorator---------------------\n\n\n');
}
function AccessorDecorator(target: any, name: string, descriptor: PropertyDescriptor) {
console.log('-------------------Accessor decorator---------------------');
console.log('target', target); // target {}
console.log('name', name); // name price
console.log('descriptor', descriptor);
// descriptor {
// get: undefined,
// set: [Function: set price],
// enumerable: false,
// configurable: true
// }
console.log('-------------------Accessor decorator---------------------\n\n\n');
}
function MethodDecorator(target: any, name: string | symbol, descriptor: PropertyDescriptor) {
console.log('-------------------Method decorator---------------------');
console.log('target', target); // target {}
console.log('name', name); // name getPriceWithTax
console.log('descriptor', descriptor);
// descriptor {
// value: [Function: getPriceWithTax],
// writable: true,
// enumerable: false,
// configurable: true
// }
const method = descriptor.value;
descriptor.value = function (...args) {
console.log('args', args);
return method.apply(this, args);
};
console.log('-------------------Method decorator---------------------\n\n\n');
}
function ParameterDecorator(target: any, name: string | symbol, position: number) {
console.log('-------------------Parameter decorator---------------------');
console.log('target', target); // target {}
console.log('name', name); // name getPriceWithTax
console.log('position', position); // position 0
console.log('-------------------Parameter decorator---------------------\n\n\n');
}
function ClassDecorator(target) {
console.log('-------------------Class decorator---------------------');
console.log('target', target); // target [class Product]
console.log('-------------------Class decorator---------------------\n\n\n');
return class extends target {
constructor() {
console.log('arguments', arguments);
super(...arguments);
}
};
}
// @ts-ignore
@ClassDecorator
class Product {
@PropertyDecorator
title: string;
private _price: number;
@AccessorDecorator
set price(val: number) {
if (val > 0) {
this._price = val;
} else {
throw new Error('Invalid price - should be positive!');
}
}
constructor(t: string, p: number) {
this.title = t;
this._price = p;
}
@MethodDecorator
getPriceWithTax(@ParameterDecorator tax: number) {
return this._price * (1 + tax);
}
}
const p1 = new Product('Book', 20);
const finallyPrice = p1.getPriceWithTax(0.1);
console.log('finallyPrice', finallyPrice);
- validator 通过 把
[target.constructor.name
propName
加到globalregisteredValidators
里面
const registeredValidators = {};
function Required(target: any, propName: string) {
registeredValidators[target.constructor.name] = {
...registeredValidators[target.constructor.name],
[propName]: ['required'],
};
}
function PositiveNumber(target: any, propName: string) {
registeredValidators[target.constructor.name] = {
...registeredValidators[target.constructor.name],
[propName]: ['positive'],
};
}
function validate(obj: any) {
const objValidatorConfig = registeredValidators[obj.constructor.name];
if (!objValidatorConfig) {
return true;
}
let isValid = true;
for (const prop in objValidatorConfig) {
for (const validator of objValidatorConfig[prop]) {
switch (validator) {
case 'required':
isValid = isValid && !!obj[prop];
break;
case 'positive':
isValid = isValid && obj[prop] > 0;
break;
}
}
}
return isValid;
}
class Course {
@Required
title: string;
@PositiveNumber
price: number;
constructor(t: string, p: number) {
this.title = t;
this.price = p;
}
}
const createdCourse = new Course('title', 1);
const result = validate(createdCourse);
console.log('result', result);
const createdCourse2 = new Course('', -1);
const result2 = validate(createdCourse2);
console.log('result2', result2);