配置/说明
typescript版本:5.9.2
tsconfig.json
配置文件
{
"compilerOptions": {
"experimentalDecorators": false, // ❌ 关闭 TypeScript 旧版实验装饰器支持(TS5 新装饰器不依赖这个)
"emitDecoratorMetadata": false, // ❌ 关闭旧的装饰器元数据生成(TS5 新装饰器不需要)
"declaration": false, // ❌ 不生成 `.d.ts` 类型声明文件,减少构建产物
"target": "ESNext", // ✅ 编译输出的 JavaScript 版本,这里选择 ESNext 保留最新语法特性(例如 class fields、private fields 等)
"module": "ESNext", // ✅ 输出模块类型为 ES 模块(import/export 语法),适合现代 JS 环境或打包工具
"useDefineForClassFields": true, // ✅ 使用 ECMAScript 标准语义定义类字段,保证类字段初始化和 TS5 装饰器兼容
"strict": true, // ✅ 启用严格模式,包含所有严格类型检查选项,提高代码安全性
"esModuleInterop": true, // ✅ 允许在 CommonJS 模块中默认导入 ES 模块,方便兼容第三方库
"removeComments": true, // ✅ 编译时移除注释,减小输出文件体积
"allowSyntheticDefaultImports": true, // ✅ 允许默认导入没有 default export 的模块,配合 esModuleInterop 使用
"sourceMap": true, // ✅ 生成 source map 文件,便于调试 TypeScript 源码
"outDir": "dist", // ✅ 编译输出目录,编译后的 JS 文件将放在 dist 文件夹
"baseUrl": "./", // ✅ 解析非相对模块导入的基准路径,通常配合 paths 使用
"skipLibCheck": true // ✅ 跳过第三方库的类型检查,减少大型项目的编译时间
},
"include": [
"src/**/*" // ✅ 指定要编译的文件或文件夹,这里是 src 下所有文件
],
"exclude": [
"node_modules" // ✅ 排除不需要编译的文件夹(通常是依赖库)
]
}
context类型
type DecoratorContext =
| ClassDecoratorContext // 类装饰器上下文
| ClassMethodDecoratorContext // 方法装饰器上下文
| ClassGetterDecoratorContext // getter 装饰器上下文
| ClassSetterDecoratorContext // setter 装饰器上下文
| ClassFieldDecoratorContext // 字段装饰器上下文
| ClassAccessorDecoratorContext// accessor 装饰器上下文
装饰器工厂
//返回一个装饰器
const decoratorFactory = () => {
console.log("类工厂=>返回一个装饰器")
return (target: any, context: ClassDecoratorContext) => {
//...
}
}
类装饰器
//类装饰器
const classDecorator = (target: any, context: ClassDecoratorContext) => {
console.log("类装饰器:原型注入/修改")
target.prototype.__name = "张三" //原型注入[__name]属性
//原型注入 [getTheName] 方法
target.prototype.getTheName = function () {
return `你好,我是:${this.__name}`;
}
}
@classDecorator
class MyClass {
}
const myClass = new MyClass();
console.log(myClass.__name); //张三
console.log(myClass.getTheName()); //你好,我是:张三
方法装饰器
function bound(originalMethod: Function, context: ClassMethodDecoratorContext) {
if (context.private) throw new Error("不支持私有方法");
//加载完成时 修改注入新方法
context.addInitializer(function (this: any,) {
//直接拷贝绑定某方法到newCopy
this["newCopy"] = this[context.name].bind(this);
//修改原方法到newGreet
this["newGreet"] = (...args: any[]) => {
const result = originalMethod.call(this, ...args);
return `巴拉巴拉--${result}`;
}
});
//修改原方法
return function (this: any, ...args: any[]) {
const result = originalMethod.call(this, ...args);
return `你好--${result}`;
};
}
class C {
message = "Hello";
@bound
greet() {
return this.message;
}
}
const c = new C();
console.log(c.greet())//你好--Hello
console.log(c.newCopy());//你好--Hello
console.log(c.newGreet())//巴拉巴拉--Hello
属性装饰器
//属性装饰器示例1
const minAge = (min: number) => {//指定最小年龄
console.log("属性工厂:创建minAge装饰器")
return function (target: any, context: ClassFieldDecoratorContext) {
console.log("属性装饰器:minAge => 拦截年龄小于18的复制的赋值")
context.addInitializer(function (this: any) {
//赋予初始值
//this[context.name]=66
//重写get set => 拦截18岁以下
// 保存初始值
let value = this[context.name];
Object.defineProperty(this, context.name, {
configurable: true,
enumerable: true,
get() {
return value;
},
set(newValue: number) {
if (newValue < min) {
throw new Error(`[${String(context.name)}] 年龄(${newValue})必须 >= ${min}`);
}
value = newValue;
}
});
});
};
}
class MyClass {
@minAge(18) age: number = 18;
}
const p: any = new MyClass();
p.age = 20
console.log(p.age)//20
try {
p.age = 17
} catch (e) {
console.log("赋值失败:", e.message);//[age] 年龄(17)必须 >= 18
}
console.log(p.age)//20
//属性装饰器示例2
const minLength = (minLength: number) => {//指定最小名称长度
return (target: any, context: ClassAccessorDecoratorContext) => {
//自定义access
return {
set(this: any, value: string) {
if (value.length < minLength) {
throw new Error(`[${String(context.name)}] 长度(${value.length})必须 >=${minLength}`);
}
return target.set.call(this, value.toUpperCase());
},
get(this: any) {
return "我的名字是:" + target.get.call(this);
}
}
}
}
const minAge = (min: number) => {//指定最小年龄
return (target: any, context: ClassFieldDecoratorContext)=> {
context.addInitializer(function (this: any) {
//赋予初始值
//this[context.name]=66
//重写get set => 拦截18岁以下
// 保存初始值
let value = this[context.name];
Object.defineProperty(this, context.name, {
configurable: true,
enumerable: true,
get() {
return value;
},
set(newValue: number) {
if (newValue < min) {
throw new Error(`[${String(context.name)}] 年龄(${newValue})必须 >=${min}`);
}
value = newValue;
}
});
});
};
}
class User {
@minLength(3) accessor name: string | undefined
@minAge(18) age: number | undefined;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
const user = new User("李元芳", 18);
console.log(user.age);// 18
try {
user.age = 16;
} catch (e) {
console.log(e.message);// [age] 年龄(16)必须 >=18
}
console.log(user.age);// 18
console.log(user.name);// 我的名字是:李元芳
try {
user.name = "张三";
} catch (e) {
console.log(e.message);// [name] 长度(2)必须 >=3
}
console.log(user.name);// 我的名字是:李元芳
装饰器-混合使用Demo
/**
* ts5 新提案装饰器
*/
const d1 = (target: any, context: any) => {
console.log("类装饰器:1")
}
const d2 = () => {
console.log("类工厂:2")
return (target: any, context: any) => {
console.log("类装饰器:2")
}
}
const d3 = () => {
console.log("类工厂:3")
return (target: any, context: any) => {
console.log("类装饰器:3")
}
}
const d4 = (target: any, context: any) => {
console.log("类装饰器:4")
}
//类装饰器
const classDecorator = (target: any, context: any) => {
console.log("类装饰器:原型注入/修改")
target.prototype.__name = "other name" //原型注入[__name]属性
//原型注入 [getTheName] 方法
target.prototype.getTheName = function () {
console.log("[getTheName] => ", this.__name);
return this.__name;
}
//原型注入 [copyMethod1] 方法 => 拷贝 MyClass[method1] 原型方法
target.prototype.copyMethod1 = function () {
return this.method1.call(this); //拷贝 => MyClass[method1] 方法
}
}
//方法装饰器
const methodDecorator = (originalMethod: any, context: any) => {
console.log("方法装饰器:注入=>方法执行前打印")
return function (this: any, ...args: any[]) {
console.log("\n[准备执行方法] =>", context.name);
const result = originalMethod.call(this, ...args);
console.log("[执行完毕] =>", context.name, "\n");
return result;
};
}
//属性装饰器
const minAge = (min: number) => {
console.log("属性工厂:创建minAge装饰器")
return function (target: any, context: ClassFieldDecoratorContext) {
console.log("属性装饰器:minAge => 拦截年龄小于18的复制的赋值")
context.addInitializer(function (this: any) {
//赋予初始值
//this[context.name]=66
//重写get set => 拦截18岁以下
// 保存初始值
let value = this[context.name];
Object.defineProperty(this, context.name, {
configurable: true,
enumerable: true,
get() {
return value;
},
set(newValue: number) {
if (newValue < min) {
throw new Error(`${String(context.name)} 必须 >= ${min}, got ${newValue}`);
}
value = newValue;
}
});
});
};
}
console.log("\n----装饰器顺序----")
//先顺序执行工厂取装饰器 => (属性装饰器) =>后倒序执行装饰器
@classDecorator
@d1
@d2()
@d3()
@d4
class MyClass {
//__name:string 由装饰器注入
//getTheName:()=>void 由装饰器注入
//copyMethod1:()=>void 由装饰器注入
name: string;
@minAge(18) age: number;
//构造器
constructor(name: string, age: number) {
this.name = name;
this.age = age;
this.temp = this.method1.bind(this); //拷贝this.method1方法=>this.temp
}
@methodDecorator
//默认方法1
method1() {
console.log(`[method1] => Hi,我是 ${this.name}.`);
}
//默认方法 打印年龄
logAge() {
console.log(`[logAge] => 我的年龄是 ${this.age}.`);
}
//默认空方法 由构造器创建
temp: () => void;
setAge(newAge: number) {
try {
this.age = newAge;// ❌ 不可以赋值=>未正常拦截
console.log(`赋值成功:${this.age}`);
} catch (e) {
console.log("赋值失败:", (e as Error).message);
}
}
}
console.log("----顺序edn----")
console.log("\n")
const p: any = new MyClass("张三", 18);
console.log("打印注入属性=>[__name]:", p.__name)//打印注入属性=>[__name]
p.getTheName();//执行注入方法 [getTheName] => 打印[__name]
p.copyMethod1();//执行注入方法 [copyMethod1] => 拷贝自方法:this.method1
p.method1();//执行默认方法 已被装饰器注入 =>执行前会打印 [准备执行方法] =>method1
p.temp();//执行空方法 已被构造器创建 => 拷贝自方法:this.method1
p.logAge();//执行默认方法 打印当前年龄
p.setAge(20)//设置年龄20 => 大于18:正常赋值 ✓
p.setAge(30)//设置年龄30 => 大于18:正常赋值 ✓
p.setAge(10)//设置年龄10 => 小于18:抛出错误 ×
p.logAge()//打印当前年龄 =>30
p.setAge(19)//设置年龄19 => 大于18:正常赋值 ✓
p.logAge()//打印当前年龄 =>19
/* 控制台打印:
----装饰器顺序----
类工厂:2
类工厂:3
属性工厂:创建minAge装饰器
方法装饰器:注入=>方法执行前打印
属性装饰器:minAge => 拦截年龄小于18的复制的赋值
类装饰器:4
类装饰器:3
类装饰器:2
类装饰器:1
类装饰器:原型注入/修改
----顺序edn----
打印注入属性=>[__name]: other name
[getTheName] => other name
[准备执行方法] => method1
[method1] => Hi,我是 张三.
[执行完毕] => method1
[准备执行方法] => method1
[method1] => Hi,我是 张三.
[执行完毕] => method1
[准备执行方法] => method1
[method1] => Hi,我是 张三.
[执行完毕] => method1
[logAge] => 我的年龄是 18.
赋值成功:20
赋值成功:30
赋值失败: age 必须 >= 18, got 10
[logAge] => 我的年龄是 30.
赋值成功:19
[logAge] => 我的年龄是 1
*/
参考文档 - 中文翻译,演示说明用途:
🌐 装饰器上下文类型(翻译与说明)
1. 装饰器总体
type DecoratorContext =
| ClassDecoratorContext // 类装饰器上下文
| ClassMethodDecoratorContext // 方法装饰器上下文
| ClassGetterDecoratorContext // getter 装饰器上下文
| ClassSetterDecoratorContext // setter 装饰器上下文
| ClassFieldDecoratorContext // 字段装饰器上下文
| ClassAccessorDecoratorContext// accessor 装饰器上下文
👉 意思:装饰器会根据目标不同,传入不同的 上下文对象,你可以通过 context.kind
判断是 类 / 方法 / 字段 / getter / setter。
2. 类装饰器 ClassDecoratorContext
kind: "class"
→ 说明是类装饰器name
→ 类名addInitializer(fn)
→ 在类定义完成后执行初始化逻辑metadata
→ 装饰器元数据
📌 用途示例:注册自定义组件
function customElement(name: string) {
return (target: any, context: ClassDecoratorContext) => {
context.addInitializer(function () {
customElements.define(name, this);
});
};
}
@customElement("my-element")
class MyElement {}
👉 在类加载完成时,把它注册到浏览器的 customElements
。
3. 方法装饰器 ClassMethodDecoratorContext
kind: "method"
→ 方法name
→ 方法名static
→ 是否静态方法private
→ 是否私有方法access.get(object)
/access.has(object)
→ 运行时获取方法引用addInitializer(fn)
→ 在方法初始化前执行
📌 用途示例:自动绑定 this
function bound(value: Function, context: ClassMethodDecoratorContext) {
if (context.private) throw new Error("不支持私有方法");
context.addInitializer(function () {
this[context.name] = this[context.name].bind(this);
});
}
class C {
message = "Hello";
@bound
greet() {
console.log(this.message);
}
}
👉 自动把方法绑定到实例,避免 this
丢失。
4. Getter 装饰器 ClassGetterDecoratorContext
kind: "getter"
name
access.get(object)
→ 调用 getteraddInitializer(fn)
→ 在 getter 可用前执行逻辑
📌 用途示例:日志拦截
function logGetter(value: any, context: ClassGetterDecoratorContext) {
return function (...args: any[]) {
console.log(`调用 getter: ${String(context.name)}`);
return value.apply(this, args);
};
}
class User {
first = "Tom";
last = "Smith";
@logGetter
get fullName() {
return `${this.first} ${this.last}`;
}
}
5. Setter 装饰器 ClassSetterDecoratorContext
kind: "setter"
name
access.set(object, value)
→ 调用 setteraddInitializer(fn)
📌 用途示例:自动校验
function minAge(min: number) {
return function (value: any, context: ClassSetterDecoratorContext) {
return function (this: any, newValue: number) {
if (newValue < min) throw new Error(`年龄必须大于 ${min}`);
return value.call(this, newValue);
}
}
}
class Person {
#age = 0;
get age() { return this.#age }
@minAge(18)
set age(v: number) { this.#age = v }
}
6. 字段装饰器 ClassFieldDecoratorContext
kind: "field"
name
→ 字段名access.get(object)
/access.set(object, value)
→ 获取 / 设置字段值addInitializer(fn)
→ 字段初始化后执行
📌 用途示例:给字段设置默认值
function defaultValue<T>(val: T) {
return function (target: any, context: ClassFieldDecoratorContext) {
context.addInitializer(function () {
if (this[context.name] === undefined) {
this[context.name] = val;
}
});
};
}
class User {
@defaultValue("Guest")
name!: string;
}
console.log(new User().name); // Guest
7. Accessor 装饰器 ClassAccessorDecoratorContext
kind: "accessor"
access.get(object)
/access.set(object, value)
→ 操作 accessor可以返回一个
ClassAccessorDecoratorResult
替换原 getter/setter/init
📌 用途示例:数据转换
function uppercase(target: any, context: ClassAccessorDecoratorContext) {
return {
set(this: any, value: string) {
return target.set.call(this, value.toUpperCase());
}
}
}
class User {
@uppercase accessor name = "";
}
const u = new User();
u.name = "tom";
console.log(u.name); // TOM
✅ 总结
类装饰器 → 操作整个类
方法装饰器 → 修改方法 / 自动绑定 this
getter/setter 装饰器 → 控制访问器逻辑
字段装饰器 → 修改字段默认值 / 校验
accessor 装饰器 → 修改
accessor
的 getter/setter
👉 这些接口就是 TypeScript 给装饰器提供的 上下文 API,帮助我们在编译期/运行时修改类和成员的行为。