前言
类装饰器不常用。
本节主要介绍装饰器的相关语法,以及一些需要注意的细节。
关键字
类装饰器、experimentalDecorators
参考资料
参数
类装饰器的本质是一个函数,该函数接收一个参数,表示该类本身(构造函数本身)。
语法
使用装饰器的语法:@得到一个函数
变量约束为类
在 TS 中,如何约束一个变量为类:
Functionnew (参数) => object「推荐」
执行时间
装饰器函数的运行时间:在类定义后直接运行
返回值
类装饰器可能的返回值:
- void:仅运行函数
- 返回一个新的类:会将新的类替换掉装饰目标
多个装饰器
执行顺序:会按照后加入先调用的顺序进行调用。
experimentalDecorators
Experimental support for decorators is a feature that is subject to change in a future release. Set the ‘experimentalDecorators’ option in your ‘tsconfig’ or ‘jsconfig’ to remove this warning.
对装饰器的实验支持是一项在将来版本中更改的功能。设置 experimentalDecorators 选项以删除此警告。
将
experimentalDecorators选项设置为 true,可以移除 vscode 中的警告信息。 问题描述:设置的experimentalDecorators不生效,如下图所示:解决办法:打开「设置」,搜索「experimental decorators」(参考资料:链接)
![]()
codes
在 TS 中,约束一个变量为类的两种写法:
Functionnew (参数) => object
function test(target: Function) {// ...new target();// ...}@testclass A { }
虽然写 Function 也是 OK 的,但是如果在函数体 test 中,通过 new 关键字来调用 test 的话,那么会报错。

function test(target: new () => object) {// ...new target();// ...}@testclass A { }
function test(target: new () => object) {console.log(target); // => [class A]}@testclass A { }const a = new A();
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {var c = arguments.length,r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc,d;if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);elsefor (var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;return c > 3 && r && Object.defineProperty(target, key, r), r;};function test(target) {console.log(target);}let A = class A {};A = __decorate([test], A);const a = new A();
编辑结果分析:
编译结果比较复杂,不过仔细观察,主要由几个部分:
- __decorate 函数
- test 函数
- A 类
__decorate 函数
__decorate 函数接收的参数:(decorators, target, key, desc)
核心关注前面两个:
- decorators:所有的装饰器
- target:这些装饰器作用的目标
decorate 函数的调用:`decorate([ test ], A)`
[ test ]:它会将所有的装饰器合并到一个数组中,作为它的第一个参数传入,这里我们只使用到一个 test 装饰器,所以数组中只有一个成员 test。A:这些装饰器作用的目标是一个类,将这个类作为第二个参数传入。
__decorate 函数的返回值是一个经过装饰器处理后的新的构造函数。
test 函数
test 函数是一个装饰器
A 类
A 最终的值将会是 __decorate 的返回值。
function test(target: new () => object) {return class B {};}@testclass A {}const a = new A();console.log(a); // => B {}
装饰器 test 的返回值,就是 A() 得到的结果。new A() 和 new B() 是等效的。
function test(target: new () => object) {return class B extends target {};}@test // ×class A {prop1: string;}const a = new A();console.log(a.prop1);

装饰函数返回类型“typeof B”不能赋给类型“void | typeof A”。类型“typeof B”不可赋给类型“typeof A”。类型“{ 0 }”中缺少属性“prop1”,但类型“A”中需要该属性。
return class B extends target {}
虽然 A 中确实含有 prop1 属性,按理来说,B 继承 A,那么 B 自然也有 prop1 属性,所以上述程序,直接看来是没有隐患的。但是 test 装饰器的参数 target 是动态的,它不一定就是 A。
这种写法,也就是在学习的时候会介绍介绍,实际开发中也不会这么写,需要知道这么写会导致的问题就可以了。
function test(target: new () => object) {}@test // ×class A {prop1: stringconstructor (public prop2: string) {}}

“typeof A”类型的参数不能赋给“new () => object”类型的参数。构造签名的类型不兼容。类型“new (prop2: string) => A”不能赋给类型“new () => object”。
test 装饰器的参数 target 约束不满足条件,所以会报错,需要修改 target 的约束条件。
new () => object:不能接收参数new (...args: any[]) => object:可以接收若干个参数
function test(des: string) {return function (target: new (...args: any[]) => object) {// ...};}@test('这是一个类')class A {prop1: string;}
@test('这是一个类') 返回值是一个装饰器
type constructor = new (...args: any[]) => object;function d1(target: constructor) {console.log("d1");}function d2(target: constructor) {console.log("d2");}@d1@d2class A {prop1: string;}
若出现多个装饰器,执行顺序会按照后加入先调用的顺序进行调用:
先输出 d2
再输出 d1
type constructor = new (...args: any[]) => object;function d1() {console.log("d1");return function (target: constructor) {console.log("d1 decorator");}}function d2() {console.log("d2");return function (target: constructor) {console.log("d2 decorator");}}@d1()@d2()class A {prop1: string;}
打印结果:
d1
d2
d2 decorator
d1 decorator
d1 和 d2 是普通的函数,它们的返回值才是装饰器。
普通的函数,写在前边的先执行。所以会先执行 d1 再执行 d2
构造器函数,写的位置与类的距离越近,越先执行。所以会先执行装饰器 d2() 再执行装饰器 d1()
解决办法:打开「设置」,搜索「experimental decorators」(参考资料:

