重拾Typescript之类的装饰器

153 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情

1.类的装饰器:

装饰器是啥?装饰第一个想到的就是emmm...女票化妆...装修...,当然笔者怎么可能让女票影响自己写blog呢?我不配,哈哈哈哈...

  • 装饰器呢,直白点就是让就好比女生的化妆品,女生通过化妆品能够变的漂亮,而类通过装饰器,让类变的也更加强大。
  • 装饰器本身其实就是一个函数

例子:

class Iverson{

}
// const sevenSixers  = new Iverson();
// console.log((sevenSixers as any).getName()) //会报错-sevenSixers.getName is not a function

function goDecorator(constructor:any){
  console.log('我是韦德')
  constructor.prototype.getName = ()=>{
    console.log('我是热火队的德怀恩-韦德')
  }
}
function nextDecorator(constructor:any){
  console.log('我是转会到骑士队的韦德')
}

@goDecorator //后被打印
@nextDecorator //先被打印
class Wade{

}

const player  =new Wade();
const player1 =new Wade();
console.log((player as any).getName()); //我是热火队的德怀恩-韦德

解析:

  • 类的装饰器使用是通过@ + 函数名放在类前面

  • 类的装饰器运行时机:类创建好之后,立刻就会去执行

  • (sevenSixers as any).getName()这种方式可以解决报错,但是不要随便用

  • 类的装饰器是针对于类的,多次实例化也只会打印出一次'我是韦德'

  • goDecorator(nextDecorator(Class Wade{}))---可以理解为近的先执行

通过工厂模式加壳子

//工厂模式
function decorator(flag:boolean){
  if(flag){
    return function(constructor:any){
      constructor.prototype.getName = ()=>{
        console.log('工厂模式')
      }
    }
  }else{
    return function(constructor:any){};
  }
}

@decorator(true)
class Go{

}
const runner = (new Go() as any).getName();
console.log(runner); // 工厂模式

解析:

  • 类似于闭包,在第一个函数内部做些处理,然后返回一个新的函数作为装饰器。
  • @decorator(true)传参数,然后在内部判断

更加优雅的方式:

为了不让typescript成为anyscript

function decorator1<T extends new (...args: any[]) => any>(constructor: T) {
  return class extends constructor{
    //这里对原来的构造函数进行了修改
    name = 'lee';
  }
}

@decorator1
class Going {
  name:string;
  constructor(name:string){
    this.name = name;
  }
}
const runner1 = (new Going('艾弗森'));
console.log(runner1); // {name:lee}

解析:

  • new的意思是这是一个构造函数,可以接受多个参数,每个参数的类型都是any

    • T extends new (...args: any[]) => any:T继承自一个构造函数
  • 打印结果不再是艾弗森,而是lee,老的构造函数会先执行,然后再去执行装饰器decorate1中的内容,从而完成了从艾弗森lee的转变

接下来看下面的例子,我们想要通过在装饰器中添加getName方法,从而让Going类的实例上都具有getName方法,为什么会失败呢?

  • 通过装饰器添加到类中的方法不会在实例化的时候被typescript识别出来
function decorator1<T extends new (...args: any[]) => any>(constructor: T) {
  return class extends constructor{
    name = 'lee';
    getName(){
      return this.name;
    }
  }
}

@decorator1
class Going {
  name:string;
  constructor(name:string){
    this.name = name;
  }
}
const runner1 = (new Going('艾弗森'));
//这里通过装饰器加入的getName方法不会被typescript识别出来
console.log(runner1.getName());

那么如果就是想要通过装饰器在类的实例中添加方法,应该如何处理呢?

来看下面例子:

function decorator1(){
  return function <T extends new (...args: any[]) => any>(constructor: T) {
    return class extends constructor{
      name = 'lee';
      getName(){
        return this.name;
      }
    }
  }
}

const Going = decorator1()(class{
  name:string;
  constructor(name:string){
    this.name = name;
  }
})

const runner1 = (new Going('艾弗森'));
//这里现在不报错了
console.log(runner1.getName());//打印lee

  • 通过让装饰器函数返回一个接收类作为参数的函数的形式来实现,高阶函数

最后注意:tscofnig.json中需要将下面的项目开启,才能够使用装饰器

"experimentalDecorators": true,