TypeScript – Decorator 装饰器
前言
TypeScript 5.0 之后就可以使用正真的 JS Decorator 了, 从前 experiment 的版本依然可用, 但是不建议继续用, 因为差很远, 一起用会混乱.
Decorator 的概念 Python 也有. 它就是设计模式里的装饰者模式, 用来扩展已有的功能.
和 Decorator 长得很像的还有 Annotation. 这个 Java 和 C# 都有 (C# 叫 Attribute).
长得像但是功能却不一样哦. Annotation 是做 meta 标签的, 通过反射获取 metadata 然后做一些逻辑处理. 这个和 Decorator 的扩展原有功能, 简直八竿子打不着一边.
TypeScript experiment 的 Decorator 很妙, 因为它有 Decorator 装饰的概念, 同时配上 reflect-metadata 还能实现 Annotation 功能. 但 5.0 后的 Decorator 移除了 Annotation 的部分, 只保留了 Decorate 的特性. Annotation 被独立了出来.
想知道它们的前世今生可以看这篇. 我就不多说了.
参考:
Medium – TS 5.0 Beta: New Decorators Are Here!
Docs – Announcing TypeScript 5.0 Beta
JavaScript metaprogramming with the 2022-03 decorators API (in-depth)
YouTube – Decorators! Coming in TypeScript 5
esbuild 目前不支持
Github Issus – Feature request: Decorators support

想尝试 Decorator 请使用 tsc,
How Decorator Look Like?
Decorator 是一个函数.
function myDecorator(target: any, context: DecoratorContext): any {
console.log([target, context.kind]);
}
调用的方式是在 class 的几个部位前, 加上 @decoratorFunction
@myDecorator
class Person {
@myDecorator
#name = ''; @myDecorator
get name(): string {
return this.#name;
} @myDecorator
set name(name: string) {
this.#name = name;
} @myDecorator
accessor age = 11; @myDecorator
method(): void {
console.log('do something...');
}
}
myDecorator 虽然是函数, 但使用的时候 @myDecorator 是不需要括弧的.
而 @createMyDecorator('some options') 则是调用一个 decorator 工厂函数, 制作出 decorator 函数.
class 许多部位都可以使用 Decorator, 比如 class, field, method, getter, setter, accessor (accessor 是非常新的东西, 专门给 decorator 用的, 太新了这篇不会介绍)
How Decorator Work? (e.g. ClassMethodDecorator)
来看一个扩展方法的 Decorator
首先是一个简单的 class 有一个方法
class Person {
sayHi(): void {
console.log('do something...');
}
}
const person = new Person();
person.sayHi();
我们想扩展这个方法. 在方法执行前后加上一些逻辑.
首先, Decorator Function 长这样
type Method = (this: unknown, ...arg: unknown[]) => unknown;
function methodDecorator(target: Method, context: ClassMemberDecoratorContext): Method {
// target 就是当前被装饰的 class 方法
const originalMethod = target; // 定义一个新方法
const decoratedMethod: Method = function (this, ...args) {
console.log('before method call'); // 扩展方法
const returnValue = originalMethod.call(this, ...args); // 调用原有方法
console.log('after method call'); // 扩展方法
return returnValue;
}; // 返回装饰后的方法
return decoratedMethod;
}
调用 Decorator
class Person {
@methodDecorator
sayHi(): void {
console.log('do something...');
}
}
当 JS 运行到 class 这里时 (注: 不是实例化的时候哦, 是 definition 的时候), methodDecorator 会被调用
sayHi 方法会被传入, 同时还有其上下文 context, 比如, 方法名, 类型 (是 class, field, method 其它...)
然后 methodDecorator 内部创建出 decoratedMethod 替换掉原来的方法.
以上就是基本的 decorator 使用方式. 你可以把它看成是 metaprogramming 的一种.
ClassDecorator
接下来, 一个一个例子看. 概念都差不多
type Class = {
new (...args: any[]): Object;
};
function myClassDecorator<TClass extends Class>(
target: TClass,
context: ClassDecoratorContext
): TClass {
return target;
}
@myClassDecorator
class Person {}
class decorator 可以装饰一个 class.
它的玩法和上面的 method decorator 一样, 输入一个 class, 返回一个 class
我们可以对这个 class 做许多事情
function myClassDecorator<TClass extends Class>(
target: TClass,
context: ClassDecoratorContext
): TClass {
// 增加一个方法给 class
target.prototype.method = function () {
console.log('add on method');
}; // 增加一个 static 属性
target['addonStaticProperty'] = 'addon static property'; // 派生类
const derivedClass = class extends target {
childValue = 'child value';
}; // 返回派生类
return derivedClass;
}
调用
@myClassDecorator
class Person {} console.log(Person['addonStaticProperty']); // 'addon static property'
console.log(Person.name); // Ali
const ali = new Person();
console.log(ali instanceof Person); // true
ali['method'](); // add on method
console.log(ali['childValue']); // child value
addInitializer
@myClassDecorator
class Person {
static value = 'default value';
}
在 static value 还没有 init 前, addInitializer 会触发
// emit before decorate class
// 这个 class static value 还没有 init, 都是 undefined
context.addInitializer(() => {
console.log(target['staticValue']); // undefined
});
至于要在这个阶段干什么, 我目前没有想到...
ClassMemberDecorator
ClassMemberDecorator 是 ClassMethod, ClassField, ClassGetter, ClassSetter, ClassAccessor 的抽象. (注: ClassMethod 上面介绍过了, 这里不再介绍)
ClassMemberDecoratorContext 同样是抽象. 它的 kind 有 method, field, getter, setter, accessor
这个是最简单的 MemberDecorator
function memberDecorator(target: any, context: ClassMemberDecoratorContext): void {}
target 有可能是 method or undefined, depend on kind.
memberDecorator 可以有 return, 也可以没有. return type 也 depend on kind.
Standard Member Info
1. target & kind
for field
class Person {
@memberDecorator
name = 'default value';
}
function memberDecorator(target: any, context: ClassMemberDecoratorContext) {
console.log(target === undefined); // true
console.log(context.kind); // field
}
for getter
@memberDecorator
get age() {
return 10;
} console.log(target); // the get method
console.log(context.kind); // getter
2. name
@memberDecorator
age = 11; console.log(context.name); // age
3. is static?
@memberDecorator
static age = 11; console.log(context.static); // true
4. is private?
@memberDecorator
get #age() {
return 11;
} console.log(context.name); // #age
console.log(context.private); // true
addInitializer
和 ClassDecorator 不同, MemberDecorator 的 addInitializer 会在每一次 class 被实例化的时候触发 (before constructor)
class Person {
constructor() {
console.log('constructor');
}
@memberDecorator
name = 'name';
}
const p1 = new Person();
const p2 = new Person();
function memberDecorator(target: any, context: ClassMemberDecoratorContext) {
context.addInitializer(() => {
console.log('call');
});
}
效果

ClassFieldDecorator
ClassMemberDecorator 是抽象, ClassFieldDecorator 是它的具体.
ClassFieldDecorator 不能用于 method, get, set 和 accessor.
.name, .kind, .private, .static 这些都和 member 一样, 这里不再复述.
好, 我们从例子里学,
readonly decorator
class Person {
@readonly
name = 'default name';
}
const p1 = new Person();
p1.name = 'new name'; // should popup error
添加 readonly decorator 到某个属性上, 这个属性就会变成 readonly.
FiledDecorator 是透过 return function 来操控 property config 的.
function readonly(target: undefined, context: ClassFieldDecoratorContext) {
// return a function for decorate property
return function (initValue: any) {
console.log(this instanceof Person); // true
return initValue; // default value
};
}
这个 return function 会在 Person 实例化时被调用 (before constructor)
return function 的 this 是 class instance, 参数是这个 property 的 init value.
function 可以返回一个加工后的 init value. 那在 constructor 阶段就会拿到新的 init value 了.
通过 class instance + property name. 我们可以做各种 property config.
function readonly(target: undefined, context: ClassFieldDecoratorContext) {
return function (initValue: any) {
Object.defineProperty(this, context.name, {
writable: false,
});
return initValue;
};
}
Object.defineProperty { writable: false } 后, 这个 property 就变成 readonly 了.
执行顺序

ClassGetterDecorator
和 ClassMehtodDecorator 大同小异.
class Person {
firstName = 'name';
lastName = 'lastName';
@getterDecorator
get fullName() {
return this.firstName + ' ' + this.lastName;
}
}
const person = new Person();
console.log(person.fullName);
function getterDecorator(target: () => any, context: ClassGetterDecoratorContext) {
const originalGetter = target;
return function () {
console.log(this instanceof Person); // true
return originalGetter.call(this) + ' decorated';
};
}
target 是原本的 getter, 返回的函数 this 指向 class instance.
TypeScript – Decorator 装饰器的更多相关文章
- Python进阶之decorator装饰器
decorator装饰器 .note-content {font-family: "Helvetica Neue",Arial,"Hiragino Sans GB&quo ...
- 谈谈Python中的decorator装饰器,如何更优雅的重用代码
众所周知,Python本身有很多优雅的语法,让你能用一行代码写出其他语言很多行代码才能做的事情,比如: 最常用的迭代(eg: for i in range(1,10)), 列表生成式(eg: [ x* ...
- Python的程序结构[8] -> 装饰器/Decorator -> 装饰器浅析
装饰器 / Decorator 目录 关于闭包 装饰器的本质 语法糖 装饰器传入参数 1 关于闭包 / About Closure 装饰器其本质是一个闭包函数,为此首先理解闭包的含义. 闭包(Clos ...
- TypeScript 素描 - 装饰器
/* 装饰器 简单理解为C#中的Attribute 可以装饰到类.函数.讯问符.属性.参数上 语法 @xxx 装饰器其实是一个函数 @xxx 就要有一个 function xxx 多个装饰器可以用来装 ...
- 使用 sitemesh/decorator装饰器装饰jsp页面(原理及详细配置)
摘要:首先这个Decorator解释一下这个单词:“装饰器”,我觉得其实可以这样理解,他就像我们用到的Frame,他把每个页面共有的东西提炼了出来,也可能我们也会用各种各样的include标签,将我们 ...
- 装饰器模式&&ES7 Decorator 装饰器
装饰器模式(Decorator Pattern)允许向一个现有的对象动态添加新的功能,同时又不改变其结构.相比JavaScript中通过鸡肋的继承来给对象增加功能来说,装饰器模式相比生成子类更为灵活. ...
- 十二、Decorator 装饰器模式
设计: 代码清单: Display public abstract class Display { public abstract int getColumns(); public abstract ...
- 项目解析1、登录验证用户是否存在 储备知识 Python 之 decorator装饰器
下面是我对 装饰器 这一小节的总结, 以及自己的理解. 注:[本文中的代码参考上述教程] 很多时候我会把Python的很多语法与C++相融合,在C++中,函数的名称即为函数的地址,我们可以通过定义成为 ...
- Python decorator装饰器
问题: 定义了一个新函数 想在运行时动态增加功能 又不想改动函数本身的代码 通过高阶段函数返回一个新函数 def f1(x): return x*2 def new_fn(f): #装饰器函数 def ...
- Python进阶: Decorator 装饰器你太美
函数 -> 装饰器 函数的4个核心概念 1.函数可以赋与变量 def func(message): print('Got a message: {}'.format(message)) send ...
随机推荐
- webpack4.15.1 学习笔记(五) — 生产环境构建
目录 生产环境构建 指定环境 生产环境构建 development和production的构建目标差异很大.dev中,需要具有实时重新加载或HMR能力的 source map 和 server.而在p ...
- oeasy教您玩转vim - 16 跳到某行
跳到某行 回忆上节课内容 上下行 向 下 是 j 向 上 是 k 上下行首 向 下 到行首非空字符 + 向 上 到行首非空字符 - 这些 motion 都可以加上 [count] 来翻倍 首尾行 首行 ...
- 题解:P10672 【MX-S1-T1】壁垒
暑期集训=依托答辩. 分析 种类数是奇数一定无解. 否则每种数字先输出一次,在此过程中每增加两个数时,因为每个数字种类数都不一样,所以前缀种类数也同时增加 \(2\),保证一定为偶数. 然后输出完以后 ...
- 如何安装 Arch Linux 操作系统?
Arch Linux Install 安装 到使用 Arch 说明前面或多或少已经接触过 Debian 系列和 Red Hat 系列相关 Linux 发行版,对于虚拟化软件 VirtualBox 如何 ...
- RHCA rh442 010 文件系统结构 BDP调优 网卡驱动带宽
文件系统结构 用户通过虚拟文件系统,访问底层的文件系统 对于一块磁盘而言,MBR + 分区表记录硬盘的信息 对于一个分区而言,这个分区的第一个块,superblock,超级块,记录分区元数据信息 对于 ...
- python面向对象:继承
python面向对象:继承super()的用 super()的用法 一: class A: def __init__(self): self.a = '这是一个属性' def add(self, x) ...
- web3 产品介绍 etherscan 区块链浏览器 将抽象的数据和理论可视化,小白也能看懂区块链
Etherscan是一个广泛使用的以太坊区块链浏览器和分析平台,它为用户提供了全面的区块链数据查询和交易分析功能.在本文中,我们将介绍Etherscan的主要特点和功能,以及如何使用它来浏览以太坊区块 ...
- 【H5】16 表单 其五 表单验证
在将数据提交到服务器之前,重要的是确保以正确的格式填写所有必需的表单控件.这称为客户端表单验证,可帮助确保所提交的数据符合各种表单控件中规定的要求.本文将引导您通过基本概念和客户端表单验证示例. 先决 ...
- 【C】Re04
一.类型限定符 extern 声明一个变量,extern声明的变量没有存储空间 const 定义一个常量,该常量必须赋值,之后且不允许更改 volatile 防止编译器优化代码??? register ...
- 从.net开发做到云原生运维(六)——分布式应用运行时Dapr
1. 前言 上一篇文章我们讲了K8s的一些概念,K8s真的是带来了很多新玩法,就像我们今天这篇文章的主角Dapr一样,Dapr也能在K8s里以云原生的方式运行.当然它也可以和容器一起运行,或者是CLI ...