TS-decorator


概念

ts 文档
With the introduction of Classes in TypeScript and ES6, there now exist certain scenarios
that require additional features to support annotating or modifying classes and class members.

Decorators provide a way to add both annotations and a meta-programming syntax
for class declarations and members

翻译: ts 和 es6 引入 class 之后,有一些场景需要的支持注解或者修改 class 和 class 的 members。Decorators 为 class 和 class member 提供了注解和元编程的语法;
装饰器的目的是改变类、方法、属性的默认行为,并不属于运行时,作用于 prototype 上,所以设计上,就不会随着不同实例而改变,以上仅个人理解,不足参考
(只能用于 class 自身的行为,与运行上下文无关)

语法

class Decorator

A Class Decorator is declared just before a class declaration.
The class decorator is applied to the constructor of the class and
can be used to observe, modify, or replace a class definition.

翻译:class decorator 作用于 class 的 constructor, 可以监听、修改、或者替换 class 的定义

语法

function classDecorator(constructor: Function) {
  // your operate
}

示例 1 - 修改 class

function sealed(constructor: Function) {
  Object.seal(constructor);
  Object.seal(constructor.prototype);
}
@sealed
class BugReport {
  type = "report";
  title: string;

  constructor(t: string) {
    this.title = t;
  }
}

示例 2 - 修改构造函数

function reportableClassDecorator<T extends { new (...args: any[]): {} }>(
  constructor: T
) {
  return class extends constructor {
    reportingURL = "http://www...";
  };
}

@reportableClassDecorator
class BugReport {
  type = "report";
  title: string;

  constructor(t: string) {
    this.title = t;
  }
}
const bug = new BugReport("Needs dark mode");
console.log(bug.title); // Prints "Needs dark mode"
console.log(bug.type); // Prints "report"
// 装饰器并不会修改原本的class 类型,bug.reportingURL,ts会报类型未定义错误;
console.log(bug.reportingURL); // Prints "http://www..."

method decorator

The decorator is applied to the Property Descriptor for the method,
and can be used to observe, modify, or replace a method definition

翻译:方法装饰器,调用方法的 Property Descriptor,可以监听、修改、或者替换方法的定义

语法

function methodDecorator(...args) {
  return function (
    target: any,
    propertyKey: string,
    descriptor: PropertyDescriptor
  ) {
    // your code
  };
}

示例

function enumerable(value: boolean) {
  return function (
    target: any,
    propertyKey: string,
    descriptor: PropertyDescriptor
  ) {
    descriptor.enumerable = value;
  };
}
class Greeter {
  greeting: string;
  constructor(message: string) {
    this.greeting = message;
  }

  @enumerable(false)
  greet() {
    return "Hello, " + this.greeting;
  }
}

访问器装饰 accessor

The accessor decorator is applied to the Property Descriptor
for the accessor and can be used to observe, modify, or replace an accessor’s definitions.

accessor 装饰器,调用 accessor 的 Property Descriptor,可以监听、修改或者替换 accessor 的定义, 注意不能同时修改同一个属性的 setter 和 getter 方法

语法

function accessorDecorator(...args) {
  return function (
    target: any,
    propertyKey: string,
    descriptor: PropertyDescriptor
  ) {
    // your code
  };
}

示例

function configurable(value: boolean) {
  return function (
    target: any,
    propertyKey: string,
    descriptor: PropertyDescriptor
  ) {
    descriptor.configurable = value;
  };
}
class Point {
  private _x: number;
  private _y: number;
  constructor(x: number, y: number) {
    this._x = x;
    this._y = y;
  }

  @configurable(false)
  get x() {
    return this._x;
  }

  @configurable(false)
  get y() {
    return this._y;
  }
}

属性装饰器 Property Decorators

The expression for the property decorator will be called as a function at runtime, with the following two arguments:

  1.Either the constructor function of the class for a static member, or the prototype of the class for an instance member.
  2. The name of the member.

语法

// 可配合reflect-metadata使用, 因为属性的使用一般是在其他的方法中,装饰器是作用域原型链中,因此想要获取修改的属性,需要先存起来,再获取
function PropertyDecorator(target, propertyKey) {
  // your code
}

示例

import "reflect-metadata";
const formatMetadataKey = Symbol("format");
function format(formatString: string) {
  return Reflect.metadata(formatMetadataKey, formatString);
}
function getFormat(target: any, propertyKey: string) {
  return Reflect.getMetadata(formatMetadataKey, target, propertyKey);
}

class Greeter {
  @format("Hello, %s")
  greeting: string;
  constructor(message: string) {
    this.greeting = message;
  }
  greet() {
    let formatString = getFormat(this, "greeting");
    return formatString.replace("%s", this.greeting);
  }
}

Parameter Decorators

语法

// 可配合reflect-metadata使用, 装饰器是作用域原型链中,因此想要获取修改的参数值,需要先存起来,再获取
function paramDecorator(
  target: any,
  propertyKey: sting | symbol,
  parameterIndex: number
) {
  // your code
}

例子

import "reflect-metadata";
const requiredMetadataKey = Symbol("required");

function required(
  target: Object,
  propertyKey: string | symbol,
  parameterIndex: number
) {
  let existingRequiredParameters: number[] =
    Reflect.getOwnMetadata(requiredMetadataKey, target, propertyKey) || [];
  existingRequiredParameters.push(parameterIndex);
  Reflect.defineMetadata(
    requiredMetadataKey,
    existingRequiredParameters,
    target,
    propertyKey
  );
}

function validate(
  target: any,
  propertyName: string,
  descriptor: TypedPropertyDescriptor<Function>
) {
  let method = descriptor.value!;

  descriptor.value = function () {
    let requiredParameters: number[] = Reflect.getOwnMetadata(
      requiredMetadataKey,
      target,
      propertyName
    );
    if (requiredParameters) {
      for (let parameterIndex of requiredParameters) {
        if (
          parameterIndex >= arguments.length ||
          arguments[parameterIndex] === undefined
        ) {
          throw new Error("Missing required argument.");
        }
      }
    }
    return method.apply(this, arguments);
  };
}

class BugReport {
  type = "report";
  title: string;

  constructor(t: string) {
    this.title = t;
  }

  @validate
  print(@required verbose: boolean) {
    if (verbose) {
      return `type: ${this.type}\ntitle: ${this.title}`;
    } else {
      return this.title;
    }
  }
}

ts decorator 语法糖代码

"use strict";
var __decorate = function (decorators, target, key, desc) {
  // 参数个数
  var c = arguments.length;
  // 参数个数小于3 class decorator r=target; 否则 属性装饰器(function、property、param)都属于属性装饰器,获取属性描述desc
  var r =
    c < 3
      ? target
      : desc === null
      ? (desc = Object.getOwnPropertyDescriptor(target, key))
      : desc;
  var d;
  // 如果用了 reflect-metadata 这个库
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
    r = Reflect.decorate(decorators, target, key, desc);
  } else {
    for (var i = decorators.length - 1; i >= 0; i--) {
      if ((d = decorators[i])) {
        // class 执行 d(constructor); 其他 (target key descriptor)
        r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
      }
    }
  }
  // 不是类装饰器,覆盖或写入key,类 返回 包裹后的 r
  return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata =
  (this && this.__metadata) ||
  function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function")
      return Reflect.metadata(k, v);
  };
var __param =
  (this && this.__param) ||
  function (paramIndex, decorator) {
    return function (target, key) {
      decorator(target, key, paramIndex);
    };
  };

如何开启

tsconfig.json

{
  "compilerOptions": {
    "target": "ES5",
    "experimentalDecorators": true,
    // 如果引入了 reflect-metadata
    "emitDecoratorMetadata": true,
    "types": ["reflect-metadata"]
  }
}

参考资料


文章作者: enochjs
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 enochjs !
 上一篇
react-native-vector-icons 安装使用 react-native-vector-icons 安装使用
介绍 react-native 字体图标库 支持字体图标库 AntDesign Entypo EvilIcons Feather FontAwesome FontAwesome 5 Fontisto Foundation Ionicons
2022-02-16
下一篇 
HSTS HSTS
HSTS是什么HSTS全称HTTP Strict-Transport-Security The HTTP Strict-Transport-Security response header (often abbreviated as HST
2021-02-26
  目录