Skip to content

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 = 1undefined初始化器函数 | 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 1

2. 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 m

3. 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 x

4. 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 1

5. 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 1

6. 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 参数: 一个包含 getset 的对象,即原型上定义的默认访问器。

返回值含义:

  • 返回一个对象 { 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) => newValueField初始化器函数,在字段赋值时转换初始值
{ get?, set?, init? }Auto-Accessor分别替换 getter、setter,并可提供初始化器
void / undefined所有类型保持原始值不变,不做任何替换

返回错误类型的后果:

  • Class 装饰器返回非可调用值 → 抛出错误
  • Method/Getter/Setter 装饰器返回非函数值 → 抛出错误
  • Field 装饰器返回非函数值 → 抛出错误
  • Auto-Accessor 装饰器返回非对象值 → 抛出错误

参考资料