typescript 文档阅读笔记 Decorators

装饰器目前正处于 stage2,如果要使用它,需要将 experimentalDecorators 置为 true。

{
  "compilerOptions": {
    "experimentalDecorators": true
  }
}

装饰器可以添加到类的声明、方法、属性、访问器、参数。使用 @expression 进行申明,这个 expression 会在运行时被调用。例如 @sealed

function sealed(target) {
  // do something with 'target' ...
}

// 使用 
@sealed

Decorator Factories

一个工厂函数,用来返回一个装饰器

function color(value: string) {
  // this is the decorator factory
  return function (target) {
    // this is the decorator
    // do something with 'target' and 'value'...
  };
}
// 使用
@color('flwkefjlew')

Decorator Composition

将装饰器组合在一起使用

@f @g x // 在同一行

@f
@g
x // 在多行
  • 每一个装饰器的表达式被计算的顺序为从上到下(当它是一个工厂函数时
  • 结果计算从下到上
function f() {
  console.log("f(): evaluated");
  return function (
    target: any,
    propertyKey: string,
    descriptor: PropertyDescriptor
  ) {
    console.log("f(): called");
  };
}

function g() {
  console.log("g(): evaluated");
  return function (
    target: any,
    propertyKey: string,
    descriptor: PropertyDescriptor
  ) {
    console.log("g(): called");
  };
}

class C {
  @f()
  @g()
  method() {}
}
// 
function f() {
  console.log("f(): evaluated");
  return function (
    target: any,
    propertyKey: string,
    descriptor: PropertyDescriptor
  ) {
    console.log("f(): called");
  };
}

function g() {
  console.log("g(): evaluated");
  return function (
    target: any,
    propertyKey: string,
    descriptor: PropertyDescriptor
  ) {
    console.log("g(): called");
  };
}

class C {
  @f()
  @g()
  method() {}
}
[LOG]: "f(): evaluated" 
[LOG]: "g(): evaluated" 
[LOG]: "g(): called" 
[LOG]: "f(): called" 

Decorator Evaluation

Class Decorators

The class decorator is applied to the constructor of the class and can be used to observe, modify, or replace a class definition。

The expression for the class decorator will be called as a function at runtime, 并将类的 constructor 作为它唯一的参数

If the class decorator returns a value, it will replace the class declaration with the provided constructor function.

注意:如果 you choose to return a new constructor function,you must take care to maintain the original prototype. The logic that applies decorators at runtime will not do this for you.

@sealed
class Greeter {
  greeting: string;
  constructor(message: string) {
    this.greeting = message;
  }
  greet() {
    return "Hello, " + this.greeting;
  }
}
function sealed(constructor: Function) {
  Object.seal(constructor);
  Object.seal(constructor.prototype);
}

例子:实现一个装饰器,用来生成一个新的类,新的类继承自被装饰的类

@classDecorator
class Greeter {
  property = "property";
  hello: string;
  constructor(m: string) {
    this.hello = m;
  }
}

function classDecorator<T extends { new (...args: any[]): {} }>(
  constructor: T
) {
  return class extends constructor {
    newProperty = "new property";
    hello = "override";
    constructor(...args: any[]) {
      super()
    }
  };
}

const greeter = new Greeter('hello')
/* greeter
{
  "property": "property",
  "hello": "override",
  "newProperty": "new property"
} 
/*

Property Decorators

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

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

NOTE  A Property Descriptor is not provided as an argument to a property decorator 因为装饰器在类定义的时候就会运行,而 property 可能在类实例化时才会有值,所以属性装饰器拿不到属性的值即不会传属性描述符作为第三个参数.The return value is ignored too. As such, a property decorator can only be used to observe that a property of a specific name has been declared for a class.

We can use this information to record metadata about the property

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);
  }
}

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);
}

Method Decorators

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

  • Either the constructor function of the class for a static member, or the prototype of the class for an instance member.
  • The name of the member.
  • The Property Descriptor for the member.

NOTE  The Property Descriptor will be undefined if your script target is less than ES5.

If the method decorator returns a value, it will be used as the Property Descriptor for the method.

NOTE  The return value is ignored if your script target is less than ES5.

class Greeter {
  greeting: string;
  constructor(message: string) {
    this.greeting = message;
  }

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

function enumerable(value: boolean) {
  return function (
    target: any, // Greeter.prototype
    propertyKey: string, // greet
    descriptor: PropertyDescriptor //  { "writable": true,"enumerable": false, "configurable": true } 
  ) {
    descriptor.enumerable = value;
  };
}

Accessor Decorators

NOTE  TypeScript disallows decorating both the get and set accessor for a single member

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

  • Either the constructor function of the class for a static member, or the prototype of the class for an instance member.
  • The name of the member.
  • The Property Descriptor for the member.

NOTE  The Property Descriptor will be undefined if your script target is less than ES5.

If the accessor decorator returns a value, it will be used as the Property Descriptor for the member.

NOTE  The return value is ignored if your script target is less than ES5.

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;
  }
}

function configurable(value: boolean) {
  return function (
    target: any,
    propertyKey: string,
    descriptor: PropertyDescriptor
  ) {
    descriptor.configurable = value;
  };
}

Parameter Decorators

The parameter decorator is applied to the function for a class constructor or method declaration.

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

  • Either the constructor function of the class for a static member, or the prototype of the class for an instance member.
  • The name of the member.
  • The ordinal index of the parameter in the function’s parameter list.

NOTE  A parameter decorator can only be used to observe that a parameter has been declared on a method. The return value of the parameter decorator is ignored.

class Greeter {
  greeting: string;

  constructor(message: string) {
    this.greeting = message;
  }

  @validate
  greet(@required name: string) {
    return "Hello " + name + ", " + this.greeting;
  }
}

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);
  };
}

Metadata

This library is not yet part of the ECMAScript (JavaScript) standard. However, once decorators are officially adopted as part of the ECMAScript standard these extensions will be proposed for adoption.

需要开启如下选项。

{
  "compilerOptions": {
    "target": "ES5",
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值