Skip to content

Typescript 装饰器与 Mixin ​

装饰器 ​

需要开启 tsconfig.json 中的 experimentalDecorators。

装饰器使用 @ 符号,必须是一个函数或求值后为函数。

装饰器工厂 ​

一个函数返回一个函数,可以接收参数:

ts
function setProps() {
  return function (target: any) {
    // ...
  }
}
@setProps()
function setProps() {
  return function (target: any) {
    // ...
  }
}
@setProps()

多个装饰器执行顺序:工厂从上往下求值,执行从下往上调用。

类装饰器 ​

可以修改类的原有属性:

ts
function classDecorator<T extends new (...args: any[]) => {}>(target: T) {
  return class extends target {
    public name = 'hello'
    public age = 18
  }
}
@classDecorator
class ClassD {
  public size = 20
  public name: string
  constructor(name: string) { this.name = name }
}
function classDecorator<T extends new (...args: any[]) => {}>(target: T) {
  return class extends target {
    public name = 'hello'
    public age = 18
  }
}
@classDecorator
class ClassD {
  public size = 20
  public name: string
  constructor(name: string) { this.name = name }
}

方法装饰器 ​

三个参数:

  1. 静态成员 → 构造函数;实例成员 → 原型对象
  2. 成员名字
  3. 属性描述符
ts
function enumerable(bool: boolean) {
  return (target: any, propName: string, descriptor: PropertyDescriptor) => {
    descriptor.enumerable = bool
  }
}
class ClassD {
  constructor(public name: string) {}
  @enumerable(false)
  public getName() { return this.name }
}
function enumerable(bool: boolean) {
  return (target: any, propName: string, descriptor: PropertyDescriptor) => {
    descriptor.enumerable = bool
  }
}
class ClassD {
  constructor(public name: string) {}
  @enumerable(false)
  public getName() { return this.name }
}

也可以替换方法实现:

ts
function enumerable(bool: boolean): any {
  return (target: any, propName: string, descriptor: PropertyDescriptor) => {
    return {
      value() { return 'not value' },
      enumerable: bool,
    }
  }
}
function enumerable(bool: boolean): any {
  return (target: any, propName: string, descriptor: PropertyDescriptor) => {
    return {
      value() { return 'not value' },
      enumerable: bool,
    }
  }
}

访问器装饰器 ​

装饰 get/set,只需在其中一个前使用:

ts
function enumerable(bool: boolean) {
  return (target: any, propName: string, descriptor: PropertyDescriptor) => {
    descriptor.enumerable = bool
  }
}
class ClassD {
  private _name: string
  constructor(name: string) { this._name = name }
  @enumerable(false)
  get name() { return this._name }
  set name(name: string) { this._name = name }
}
function enumerable(bool: boolean) {
  return (target: any, propName: string, descriptor: PropertyDescriptor) => {
    descriptor.enumerable = bool
  }
}
class ClassD {
  private _name: string
  constructor(name: string) { this._name = name }
  @enumerable(false)
  get name() { return this._name }
  set name(name: string) { this._name = name }
}

属性装饰器 ​

两个参数(原型对象、属性名),无法修改属性描述符:

ts
function print(target: any, propertyName: string) {
  console.log(propertyName)
}
class ClassD {
  @print
  private name: string
}
function print(target: any, propertyName: string) {
  console.log(propertyName)
}
class ClassD {
  @print
  private name: string
}

参数装饰器 ​

三个参数(原型对象、方法名、参数索引),返回值被忽略:

ts
function required(target: any, propertyName: string, index: number) {
  console.log(`修饰的是第${index + 1}个参数`)
}
class ClassD {
  public name: string = 'hhh'
  getInfo(prefix: string, @required infoType: string): any {}
}
function required(target: any, propertyName: string, index: number) {
  console.log(`修饰的是第${index + 1}个参数`)
}
class ClassD {
  public name: string = 'hhh'
  getInfo(prefix: string, @required infoType: string): any {}
}

Mixin ​

JS 中的 Mixin ​

ts
class A { getA() {} }
class B { getB() {} }

const mixin = (target: any, from: any) => {
  Object.getOwnPropertyNames(from).forEach(key => {
    target[key] = from[key]
  })
}
mixin(A.prototype, B.prototype)
class A { getA() {} }
class B { getB() {} }

const mixin = (target: any, from: any) => {
  Object.getOwnPropertyNames(from).forEach(key => {
    target[key] = from[key]
  })
}
mixin(A.prototype, B.prototype)

TS 中的 Mixin ​

使用 implements 而不是 extends,需要在类中实现:

ts
class A {
  public isA: boolean
  public getA() {}
}
class B {
  public isB: boolean
  public getB() {}
}

class ClassAB implements A, B {
  public isA: boolean = false
  public isB: boolean = false
  public getA: () => void
  public getB: () => void
  constructor() {}
}

function mixins(base: any, from: any[]) {
  from.forEach(item => {
    Object.getOwnPropertyNames(item.prototype).forEach(key => {
      base.prototype[key] = item.prototype[key]
    })
  })
}
mixins(ClassAB, [A, B])
class A {
  public isA: boolean
  public getA() {}
}
class B {
  public isB: boolean
  public getB() {}
}

class ClassAB implements A, B {
  public isA: boolean = false
  public isB: boolean = false
  public getA: () => void
  public getB: () => void
  constructor() {}
}

function mixins(base: any, from: any[]) {
  from.forEach(item => {
    Object.getOwnPropertyNames(item.prototype).forEach(key => {
      base.prototype[key] = item.prototype[key]
    })
  })
}
mixins(ClassAB, [A, B])