简介
TypeScript 中的装饰器(Decorators)提供了一种实现注解(annotations)机制和元编程(meta-programming)的语法。
如果要使用装饰器这个特性,需要在编译 ts 时增加相应的配置。experimentalDecorators
需要配置为 true
{ "compilerOptions": { "target": "ES5", "experimentalDecorators": true } }
装饰器可以用在如下场合:
- 类定义(class declaration)
- 方法(method)
- 访问器(accessor)
- 属性(property)
- 参数(parameter)
类装饰器
我们先看一个简单的例子,看看装饰器是怎么用的以及能干什么事情。
@sealed class Person { name: string; constructor(name: string) { this.name = name; } speak() { console.log('Hello, my name is ' + this.name); } }
在上面代码中,第一行的 @sealed
就是装饰器,用来装饰下一行的 Person
类型,从名字上看,sealed
应该是把和 Person
相关的对象密封了,但是具体行为是怎么样的还要看这个装饰器是如何实现的。
本质上讲,一个装饰器就是一个函数,不同类型的装饰器接受特定的参数。下面是 sealed
的具体实现:
function sealed(constructor: new(...args: any[]) => {}) { Object.seal(constructor); Object.seal(constructor.prototype); }
这里,sealed
方法将构造函数以及构造函数的 prototype
属性都被密封了,从而禁止了对象的扩展。
接下来我们看看如何通过类装饰器重载原有的构造函数
function modifyConstructor<T extends new(...args: any[]) => {}>(constructor: T) { return class extends constructor { hometown = 'Beijing' }; } @modifyConstructor class Person { name: string; constructor(name: string) { this.name = name; } speak() { console.log('Hello, my name is ' + this.name); } } interface ExtPerson extends Person { hometown: string; }; const p = new Person('老三') as ExtPerson; console.log(p.hometown); // Beijing
类装饰器写在类声明的前一行,可以用来修改或者替换原来的类构造函数。装饰器函数只接受一个参数,即类的构造函数。如果累装饰器函数有返回值,那么这个返回值将会做为新的构造函数替代原有的内容。
这里有两点需要注意:
- 装饰器函数
modifyConstructor
使用了泛型用来描述参数类型,泛型T
扩展了构造函数的基础类型定义。这样才能支持任意参数格式的构造函数。 - 对于通过装饰器扩展的属性,ts 是无法获悉的,因此这里额外增加了一个接口声明
ExtPerson
来描述p
的类型,否则使用p.hometown
会提示错误。
留言