如果没有用过装修器,很多人应该跟我一样对装修器详细是个什么东西没什么概念,只知道它是一个样子像 @decorator 的东西,会对被装修的东西产生影响,可是详细是怎么影响的不清楚。那么,就先用尽量简洁的言语来解说什么是装修器:

装修器本质上便是一个包装函数。它能够拿到被装修的内容,对其进行自界说加工和处理。

一个抽象的解说之后,来看一个简略的比如:

// 装修器完成
function decorateClass<T>(constructor: T) {
  console.log('decorateClass');
}
// 作为类装修器
@decorateClass
class A {
  constructor() {}
}
// 输出成果: decorateClass

装修器能够拿到类 A,对其进行加工。

装修器的完成

刚刚已经看了一个简略的装修器比如,可是装修器的完成其实不止这种方式,简略总结便是:

  1. 不支持参数的装修器

    function decorator(target) {
      // do something with 'target' ...
    }
    
  2. 支持参数的装修器(装修器工厂) 其实是一个工厂函数,回来一个装修器办法。

    function decorator(value: string) {
      // this is the decorator factory, it sets up
      // the returned decorator function
      return function (target) {
        // this is the decorator
        // do something with 'target' and 'value'...
      };
    }
    

装修器能够使用在哪些目标上?

知道装修器的基本概念之后,自然也需要知道到底能够用来装修什么东西,简略总结便是类相关的它都能够装修:

  • 类(Class Decorators)
  • 类特点(Property Decorators)
  • 类拜访器(Accessor Decorators)
  • 类办法(Method Decorators)
  • 类办法参数(Parameter Decorators)

类装修器 – Class Decorators

效果:能够拿到类的结构函数,能够修正或替换类的界说

效果目标:类

参数:类的结构函数

回来值:如果带回来值,则需要是一个结构函数,该结构函数将替换原本类的完成

如下示例,回来一个新的类替换原本类的完成:

function decorateClass(constructor) {
  return class B extends constructor {
    name = 'B';
    age = 18;
  }
}
@decorateClass
class A {
  name = 'A';
  constructor() {
  }
}
const a = new A();
console.log(a.name, a.age)  // 输出 B 18
function decorateClass(name) {
  return function (constructor) {
    return class B extends constructor {
      name = name || 'B';
      age = 18;
    };
  };
}
@decorateClass('name')
class A {
  name = 'A';
  constructor() {}
}
const a = new A();
console.log(a.name, a.age); // 输出 name 18

类特点装修器 – Property Decorators

效果:能够拿到特点名,对特点完成署理等操作

效果目标:静态成员、实例成员

参数

  1. target:关于静态成员则是结构函数,关于实例成员则是类的原型
  2. key:成员名

回来值:无

如下示例,对装修的特点完成署理,检查设置的值是否合法:

function validProperty(validNameList: string[]) {
  return function (target: any, propertyKey: string) {
    let value: string = target[propertyKey];
    const get = function () {
      console.log(`${propertyKey} value: ${value}`);
      return value;
    };
    const set = function (val: string) {
      console.log('validNameList', validNameList);
      if (!validNameList.includes(val)) {
        console.warn(`invalid name: ${val}`);
        return;
      }
      value = val;
    };
    Object.defineProperty(target, propertyKey, { set, get });
  };
}
class People {
  @validProperty(['a', 'b'])
  name: string;
  constructor(name: string) {
    this.name = name;
  }
}
const people = new People('a');
console.log(people.name); // a
people.name = 'cccc'; // invalid name: cccc
console.log(people.name); // a

类拜访器装修器 – Accessor Decorators

效果:能够修正或替换拜访器的界说

效果目标:静态拜访器、实例拜访器

参数

  1. target:关于静态拜访器则是结构函数,关于实例拜访器则是类的原型
  2. key:拜访器名
  3. descriptor:特点描述符,即Object.getOwnPropertyDescriptor(target,key)

回来值:如果带回来值,则需要是一个特点描述符,并作为拜访器的特点描述符

如下示例,修正拜访器的特点描述符:

function enumerable(value: boolean) {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    descriptor.enumerable = value;
  };
}
class People {
  @validProperty(['a', 'b'])
  firstName: string;
  lastName: string;
  @enumerable(false)
  get fullName() {
      return `${this.firstName} ${this.lastName}`;
  }
  constructor(firstName: string, lastName: string) {
    this.firstName = firstName;
    this.lastName = lastName;
  }
}

类办法装修器 – Method Decorators

效果:能够修正或替换办法的界说

效果目标静态办法、实例办法

参数

  1. target:关于静态办法则是结构函数,关于实例办法则是类的原型
  2. key:办法名
  3. descriptor:特点描述符,即Object.getOwnPropertyDescriptor(target,key)

回来值:如果带回来值,则需要是一个特点描述符,并作为拜访器的特点描述符

如下示例,增强办法,弥补办法弃用提示:

const deprecated = (deprecationReason: string) => {
  return (target: any, memberName: string, propertyDescriptor: PropertyDescriptor) => {
    const wrapperFn = (...args: any[]) => {
        console.warn(`Method ${memberName} is deprecated with reason: ${deprecationReason}`);
        propertyDescriptor.value.apply(target, args)
    }
    return {
      value: wrapperFn,
    }
  }
}
class TestClass {
  static staticMember = true;
  instanceMember: string = "hello"
  @deprecated("Use another static method")
  static deprecatedMethodStatic() {
    console.log('inside deprecated static method - staticMember =', this.staticMember);
  }
  @deprecated("Use another instance method")
  deprecatedMethod () {
    console.log('inside deprecated instance method - instanceMember =', this.instanceMember);
  }
}
TestClass.deprecatedMethodStatic();
const instance = new TestClass();
instance.deprecatedMethod();

类办法参数装修器 – Parameter Decorators

效果:待弥补

效果目标:静态办法、实例办法

参数

  1. target:关于静态成员则是结构函数,关于实例成员则是类的原型
  2. key:参数名
  3. index:参数在函数参数列表中的索引

回来值:无

如下示例:

function print(target: Object, propertyKey: string, parameterIndex: number) {
  console.log(`Decorating param ${parameterIndex} from ${propertyKey}`);
}
class TestClass {
  testMethod(param0: any, @print param1: any) {}
}

小结

快速了解 TS 装修器

装修器的使用

待弥补

参考

  • Decorators
  • Typescript 装修器及使用场景浅析