TC39 Stage 3 装饰器类型详解
概述
TC39 Stage 3 装饰器提案定义了六种装饰器类型,每种装饰器接收不同的 value 参数,并且对返回值有不同的要求。 理解返回值的含义是正确使用装饰器的关键。
装饰器类型总览
| 装饰器类型 | 应用于 | value 参数 | 返回值 |
|---|---|---|---|
| Class 装饰器 | class C {} | 类本身(Function) | 新的类/可调用对象 | void |
| Method 装饰器 | m() {} | 方法函数(Function) | 新的方法函数 | void |
| Getter 装饰器 | get x() {} | getter 函数(Function) | 新的 getter 函数 | void |
| Setter 装饰器 | set x(v) {} | setter 函数(Function) | 新的 setter 函数 | void |
| Field 装饰器 | x = 1 | undefined | 初始化器函数 | void |
| Auto-Accessor 装饰器 | accessor x = 1 | { get, set } 对象 | { get?, set?, init? } 对象 | void |
各类型详细说明
1. Class 装饰器
ts
type ClassDecorator = (value: Function, context: {
kind: "class";
name: string | undefined;
addInitializer(initializer: () => void): void;
}) => Function | void;value 参数: 被装饰的类本身。
返回值含义:
- 返回
Function(新的类或可调用对象):替换原始类。新返回的值必须是可调用的(类、函数或 Proxy),否则抛出错误 - 返回
void(或不返回):保持原始类不变
示例:
js
function logged(value, { kind, name }) {
if (kind === "class") {
// 返回一个新类来替换原始类
return class extends value {
constructor(...args) {
super(...args);
console.log(`constructing an instance of ${name} with arguments ${args.join(", ")}`);
}
}
}
}
@logged
class C {}
new C(1);
// constructing an instance of C with arguments 12. Method 装饰器
ts
type ClassMethodDecorator = (value: Function, context: {
kind: "method";
name: string | symbol;
access: { get(): unknown };
static: boolean;
private: boolean;
addInitializer(initializer: () => void): void;
}) => Function | void;value 参数: 被装饰的方法函数。
返回值含义:
- 返回
Function(新函数):替换原始方法。新函数会被放置到原型上(静态方法则放在类本身上)。如果返回的不是函数类型,抛出错误 - 返回
void:保持原始方法不变
示例:
js
function logged(value, { kind, name }) {
if (kind === "method") {
// 返回新函数包装原始方法
return function (...args) {
console.log(`starting ${name} with arguments ${args.join(", ")}`);
const ret = value.call(this, ...args);
console.log(`ending ${name}`);
return ret;
};
}
}
class C {
@logged
m(arg) {}
}
new C().m(1);
// starting m with arguments 1
// ending m3. Getter 装饰器
ts
type ClassGetterDecorator = (value: Function, context: {
kind: "getter";
name: string | symbol;
access: { get(): unknown };
static: boolean;
private: boolean;
addInitializer(initializer: () => void): void;
}) => Function | void;value 参数: 原始的 getter 函数。
返回值含义:
- 返回
Function(新函数):替换原始 getter。新函数会被放置到原型上替代原始 getter(静态则放在类本身上)。如果返回的不是函数类型,抛出错误 - 返回
void:保持原始 getter 不变
注意: Getter 装饰器的行为本质上和 Method 装饰器一致——都是"接收一个函数,返回一个替换函数"。 这与 Auto-Accessor 装饰器不同,后者才需要返回包含
get/set的对象。
示例:
js
function logged(value, { kind, name }) {
if (kind === "getter") {
// 返回新的 getter 函数
return function (...args) {
console.log(`getting ${name}`);
const ret = value.call(this, ...args);
return ret;
};
}
}
class C {
@logged
get x() { return 42; }
}
new C().x;
// getting x4. Setter 装饰器
ts
type ClassSetterDecorator = (value: Function, context: {
kind: "setter";
name: string | symbol;
access: { set(value: unknown): void };
static: boolean;
private: boolean;
addInitializer(initializer: () => void): void;
}) => Function | void;value 参数: 原始的 setter 函数。
返回值含义:
- 返回
Function(新函数):替换原始 setter。规则与 getter 装饰器相同 - 返回
void:保持原始 setter 不变
注意: Getter 和 Setter 装饰器是分别独立应用的。 在同一个属性上,
@foo放在get x()上只装饰 getter,set x()不受影响。
示例:
js
function logged(value, { kind, name }) {
if (kind === "setter") {
return function (...args) {
console.log(`setting ${name} with arguments ${args.join(", ")}`);
const ret = value.call(this, ...args);
return ret;
};
}
}
class C {
@logged
set x(arg) {}
}
new C().x = 1;
// setting x with arguments 15. Field 装饰器
ts
type ClassFieldDecorator = (value: undefined, context: {
kind: "field";
name: string | symbol;
access: { get(): unknown, set(value: unknown): void };
static: boolean;
private: boolean;
addInitializer(initializer: () => void): void;
}) => (initialValue: unknown) => unknown | void;value 参数: undefined。字段没有直接的输入值可供装饰。
返回值含义:
- 返回一个初始化器函数
(initialValue) => newValue:该函数在字段被赋值时执行,接收字段的初始值,返回新的初始值。如果返回的不是函数类型,抛出错误 - 返回
void:保持字段初始值不变
注意: Field 装饰器与其他装饰器不同,它不是替换一个已有的值,而是提供一个初始化器来转换字段的初始值。
示例:
js
function logged(value, { kind, name }) {
if (kind === "field") {
// 返回初始化器函数
return function (initialValue) {
console.log(`initializing ${name} with value ${initialValue}`);
return initialValue;
};
}
}
class C {
@logged x = 1;
}
new C();
// initializing x with value 16. Auto-Accessor 装饰器
ts
type ClassAutoAccessorDecorator = (
value: {
get: () => unknown;
set(value: unknown) => void;
},
context: {
kind: "accessor";
name: string | symbol;
access: { get(): unknown, set(value: unknown): void };
static: boolean;
private: boolean;
addInitializer(initializer: () => void): void;
}
) => {
get?: () => unknown;
set?: (value: unknown) => void;
init?: (initialValue: unknown) => unknown;
} | void;value 参数: 一个包含 get 和 set 的对象,即原型上定义的默认访问器。
返回值含义:
- 返回一个对象
{ get?, set?, init? }:get:替换原始 getter,拦截属性读取set:替换原始 setter,拦截属性写入init:初始化器函数,用于修改私有槽中的初始值(类似 Field 装饰器的初始化器)- 省略的属性保持原始行为不变
- 如果返回的对象不包含这些属性之外的内容,或返回了非对象类型,抛出错误
- 返回
void:保持原始行为不变
注意: 这是唯一一种返回对象的装饰器。
accessor关键字是 Stage 3 提案引入的新语法, 它会自动生成 getter/setter 对和一个私有存储槽。
示例:
js
function logged(value, { kind, name }) {
if (kind === "accessor") {
let { get, set } = value;
// 返回对象,包含新的 get/set/init
return {
get() {
console.log(`getting ${name}`);
return get.call(this);
},
set(val) {
console.log(`setting ${name} to ${val}`);
set.call(this, val);
},
init(initialValue) {
console.log(`initializing ${name} with value ${initialValue}`);
return initialValue;
}
};
}
}
class C {
@logged accessor x = 1;
}
let c = new C();
// initializing x with value 1
c.x;
// getting x
c.x = 123;
// setting x to 123返回值规则总结
所有装饰器都可以选择不返回任何值(void),此时使用原始的未装饰值。
| 返回值类型 | 适用的装饰器 | 含义 |
|---|---|---|
Function(可调用对象) | Class、Method、Getter、Setter | 替换原始值。对于 Class 必须是可调用的;对于 Method/Getter/Setter 必须是函数 |
(initialValue) => newValue | Field | 初始化器函数,在字段赋值时转换初始值 |
{ get?, set?, init? } | Auto-Accessor | 分别替换 getter、setter,并可提供初始化器 |
void / undefined | 所有类型 | 保持原始值不变,不做任何替换 |
返回错误类型的后果:
- Class 装饰器返回非可调用值 → 抛出错误
- Method/Getter/Setter 装饰器返回非函数值 → 抛出错误
- Field 装饰器返回非函数值 → 抛出错误
- Auto-Accessor 装饰器返回非对象值 → 抛出错误