跳到主要内容

TypeScript 装饰器

装饰器(Decorator)是一种特殊类型的声明,它能够被附加到类声明、方法、访问器、属性或参数上。装饰器使用 @expression这种形式,expression求值后必须为一个函数,它会在运行时被调用,被装饰的声明信息做为参数传入。

通过装饰器,我们可以修改类的行为,甚至可以实现 AOP(面向切面编程)。TypeScript 支持 5 种装饰器:

类装饰器

类装饰器在类声明之前被声明。类装饰器应用于类构造函数,可以用来监视、修改或替换类定义。

function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}

@sealed
class BugReport {
type = 'report';
title: string;

constructor(t: string) {
this.title = t;
}
}

上面代码中,@sealed就是一个装饰器。它修改了BugReport这个类的行为,为它加上了封印,阻止了添加新属性。

方法装饰器

方法装饰器声明在一个方法的声明之前。它会被应用到方法的属性描述符上,可以用来监视、修改或者替换方法定义。

function enumerable(value: boolean) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
descriptor.enumerable = value;
};
}

class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}

@enumerable(false)
greet() {
return 'Hello, ' + this.greeting;
}
}

上面代码中,@enumerable是一个装饰器,它修改了greet方法的描述对象,使得该方法不可枚举。

访问器装饰器

访问器装饰器声明在一个访问器的声明之前。访问器装饰器应用于访问器的属性描述符,可以用来监视、修改或替换一个访问器的定义。

function configurable(value: boolean) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
descriptor.configurable = value;
};
}

class Point {
private _x: number;
private _y: number;
constructor(x: number, y: number) {
this._x = x;
this._y = y;
}

@configurable(false)
get x() {
return this._x;
}

@configurable(false)
get y() {
return this._y;
}
}

上面代码中,@configurable装饰器修改了xy访问器的描述对象,使得它们不可配置。

属性装饰器

属性装饰器声明在一个属性声明之前。属性装饰器不能用在声明文件中,或者任何外部上下文里。

function format(formatString: string) {
return function (target: any, propertyKey: string) {
let value = this[propertyKey];

function getter() {
return formatString.replace('%s', value);
}

function setter(newVal: string) {
value = newVal;
}

Object.defineProperty(target, propertyKey, {
get: getter,
set: setter,
});
};
}

class Greeter {
@format('Hello, %s')
greeting: string;

constructor(message: string) {
this.greeting = message;
}

greet() {
return this.greeting;
}
}

let g = new Greeter('world');
console.log(g.greet()); // "Hello, world"

上面代码中,@format装饰器修改了greeting属性的行为,使得它在获取和设置时都会进行格式化。

参数装饰器

参数装饰器声明在一个参数声明之前。参数装饰器应用于类构造函数或方法声明。

function required(target: Object, propertyKey: string | symbol, parameterIndex: number) {
throw new Error(`Argument ${parameterIndex} of ${propertyKey.toString()} is required`);
}

class Greeter {
greeting: string;

constructor(message: string) {
this.greeting = message;
}

greet(@required name: string) {
return 'Hello ' + name + ', ' + this.greeting;
}
}