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 }
}方法装饰器
三个参数:
- 静态成员 → 构造函数;实例成员 → 原型对象
- 成员名字
- 属性描述符
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])
Cao Note