草庐IT

【TypeScript】TS进阶-装饰器(九)

不叫猫先生 2023-04-20 原文

🐱个人主页:不叫猫先生
🙋‍♂️作者简介:前端领域新星创作者、阿里云专家博主,专注于前端各领域技术,共同学习共同进步,一起加油呀!
💫系列专栏:vue3从入门到精通TypeScript从入门到实践
📢资料领取:前端进阶资料以及文中源码可以找我免费领取
🔥社群招募:博主建立了一个前端交流群,汇集了各路大神,期待你的加入!(文末有我wx,或者私我)

目录

专栏介绍

TypeScript从入门到实践专栏是博主在学习和工作过程中的总结,实用性非常强,内容会不断进行精进,欢迎订阅哦,学会TS不迷路。

TS系列标题
基础篇TS入门(一)
基础篇TS类型声明(二)
基础篇TS接口类型(三)
基础篇TS交叉类型&联合类型(四)
基础篇TS类型断言(五)
基础篇TS类型守卫(六)
进阶篇TS函数重载(七)
进阶篇TS泛型(八)
进阶篇TS装饰器(九)

装饰器

**装饰器(Decorator)**是一种特殊类型的声明,它能够被附加到类声明、方法、属性或参数上,装饰器的本身。其本身是一个函数,会在运行的时候被调用,被装饰的声明信息会作为参数传递给装饰器函数,当作形参。装饰器本质上主要是在操作原型对象,通过给原型对象 prototype添加一些方法和属性,来扩展类的功能。
装饰器主要分为类装饰器(接收1个参数)、属性装饰器(接收2个参数)、方法装饰器(接收3个参数、参数装饰器(接收3个参数),不同装饰器接收参数也不一同。另外TS内置了装饰器类型,我们直接用就好了。

装饰器分类ts内置装饰器类型接收参数
类装饰器ClassDecorator1个,类函数
方法装饰器MethodDecorator3个,类函数,方法名,成员属性描述符
属性装饰器PropertyDecorator2个,类函数、属性名称
参数装饰器ParameterDecorator3个,类函数,参数名,参数所在位置的索引

要想在 TypeScript 中使用装饰器,必须将 tsconfig.jsonexperimentalDecorators设置为true,具体如下所示:

  • 生成tsconfig.json文件
tsc --init
  • 配置
tsc --target ES5 --experimentalDecorators
{
  "compilerOptions": {
    "target": "ES5",
    "experimentalDecorators": true,                   /* Enable experimental support for TC39 stage 2 draft decorators. */
    "emitDecoratorMetadata": true,                    /* Emit design-type metadata for decorated declarations in source files. */
    }
 }
  • 语法
    @expressionexpression是一个函数,位置根据不同类型的装饰器要求放置。

1、类装饰器

(1)一个装饰器

类装饰器接收一个构造函数作为参数,参数的类型是一个函数。

function ClassDecorator(target: Function): void {
  target.prototype.start = function(): void {
      // 通用功能
      console.log('1')
  }
}
@ClassDecorator
class Course {
  constructor() {
      // 业务逻辑
  }
}
let course = new Course();
console.log('course',course.start())

(2)装饰器累加

装饰器不是只能定义一个,可以定义多个作用于一个类函数,通过装饰器累加从而给类追加多个方法和属性,可以用来监视、修改、替换类定义。

  • 参数:只有target一个,意思是类函数本身
function StartTime(target: Function): void {
  target.prototype.start = function(): void {
      // 通用功能
      console.log('start')
  }
}
function EtartTime(target: Function): void {
  target.prototype.end = function(): void {
      // 通用功能
      console.log('end')
  }
}
@StartTime
@EndTime
class Course {
  constructor() {
      // 业务逻辑
  }
}
let course = new Course();
console.log('course',course.start())
console.log('course',course.end())

(3)装饰器传参

装饰器传参,那么怎么接受呢,参数如何放置,只需要在内部再返回一个装饰器就可,参数在外部进行接收,具体例子如下所示:

function ClassDecorator(name: string) {
  return (target: any)  => {
    target.prototype.name = name
  }
}
@ClassDecorator('zhangsan')
class Course {
  constructor() {
      // 业务逻辑
  }
}
let course:p = new Course();
interface p {
   name?:string
}
console.log('course',course.name)//zhangsan

2、方法装饰器

方法装饰器使用与类装饰器基本相同,方法装饰器需要放在类方法的前面,方法作为参数传给方法装饰器,接收三个参数,具体如下:

  • target: 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  • propertyKey: 方法名称。
  • descriptor: 成员的属性描述符。

//PropertyDescriptor类型是ts内置的
function MethodsDecorator (target: Object, propertyKey: string, descriptor: PropertyDescriptor){
     console.log(target)
     console.log(propertyKey)
     console.log(descriptor)
}

class Person {
    name: string = ''
    age: number = 0

    constructor(name: string, age: number) {
        this.name = name
        this.age = age
    }

    @MethodsDecorator
    getName() {
      return this.name
    }

    @MethodsDecorator
    getAge() {
      return this.age
    }
}

const p = new Person('张三', 18)
p.getName() // getName:张三
p.getAge() // getAge:18


我们可以看到打印出来的值,getNamegetAge被一一打印出来以及属性描述符。其中属性描述符主要包括以下四个属性:

  • configurable:是否可删除
  • enumerable:是否可枚举
  • value:属性值
  • writable:是否可修改

3、属性装饰器

属性装饰器只接收两个参数,具体如下:

  • target: 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  • propertyKey:属性名称
const propertyName = (target: Object, propertyKey: string) => {
  console.log(target, propertyKey)
}

class Person {
    @propertyName 
    name: string = ''
    @propertyName
    age: number = 0

    constructor(name: string, age: number) {
        this.name = name
        this.age = age
    }
}
const p = new Person('张三', 18)

打印结果如下所示:

注意:如果@propertyName 只写在name: string = ''上面,则只会打印出name相关的数据。

4、参数装饰器

参数装饰器用于装饰函数的参数,与方法装饰器一样接收三个参数,具体如下:

  • target:对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  • propertyKey:属性名称。
  • paramIndex::参数所在位置的索引
const paramDecorator = (target: any, propertyKey: string, paramIndex: number) => {
    console.log(target, propertyKey, paramIndex)
}

class Person {
    name: string = '';
    age: number = 0;
    
    constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }

    setName(age:number,@paramDecorator name: string) {
      this.name = name;
      this.age = age;
    }
}

const p = new Person('张三', 18) 
p.setName(1,'李四')

打印结果如下所示:

5、实际应用

(1)错误信息自定义

怎么使用装饰器进行错误信息自定义,这里使用方法装饰器来对传进来的函数进行处理,主要步骤如下:

  • 解构参数
  • 取到传进来的函数
  • 使用try...catch执行函数,新的报错定义在catch
  const ErrorDecorator:MethodDecorator = (...args: any[]) => {

    const [,,descriptor] = args;
    const method = descriptor.value
    descriptor.value = () =>{
      try {
        method()
      } catch (error) {
        console.log('这是新定义的错误')
      }    
    }
  }

  class User {
    @ErrorDecorator
    public errorMsg () {
      throw new Error('报错了')
    }

  }
  new User().errorMsg()

在使用装饰器时先将原有的值进行存储一下,再去使用,以此来确保它使用的是类中的方法中的值。

有关【TypeScript】TS进阶-装饰器(九)的更多相关文章

  1. ruby-on-rails - Spree 模块装饰器 - 2

    我正在为我的在线商店使用SpreeCommerce。我想在结帐过程中更改一些行为,这些行为在spreegem内的app/models/spree/order/checkout.rb中定义。所以我在我的应用程序中的同一点创建了一个checkout_decorator.rb。问题是,我的更改没有加载。另一个问题是,模块内的所有内容都在一个方法内,即defself.included(klass)方法。所以我想我必须覆盖整个文件,而不是只覆盖一种方法。这是我的装饰器的样子:checkout_decorator.rbSpree::Order::Checkout.module_evaldodefs

  2. ruby - 在将字符串写入日志文件之前从字符串中删除颜色装饰 - 2

    我这样使用ruby​​记录器:$logger=Logger.newMultiIO.new($stdout,log_file)MultiIO是我从thisanswer得到的一个类.这在大多数情况下效果很好,但我正在使用'colored'ruby​​gem在终端上提供彩色输出。不幸的是,这也最终出现在日志文件中,因为ANSI转义看起来像[32mPASS[0m或一些类似的不可打印字符垃圾。清理日志文件字符串同时保持tty字符串颜色的最佳方法是什么?我不介意猴子修补Logger或MultiIO,但我绝对不希望对日志文件和屏幕进行两次不同的调用。 最佳答案

  3. 大家沉迷短视频无法自拔?Python爬虫进阶,带你玩转短视频 - 2

    大家好,我是辣条。现在短视频可谓是一骑绝尘,吃饭的时候、休息的时候、躺在床上都在刷短视频,今天给大家带来python爬虫进阶:美拍视频地址加密解析。短视频js逆向解析抓取目标工具使用重点学习内容项目思路解析抓取目标目标网址:美拍视频工具使用开发环境:win10、python3.7开发工具:pycharm、Chrome工具包:requests、xpath、base64重点学习内容爬虫采集数据的解析过程js代码调试技巧js逆向解析代码Python代码的转换项目思路解析进入到网站的首页挑选你感兴趣的分类根据首页地址获取到进入详情页面的超链接的跳转地址找到对应加密的视频播放地址数据这个数据是静态的网页

  4. python - Ruby 中的装饰器(从 Python 迁移) - 2

    我今天从Python的角度学习Ruby。我完全没能解决的一件事是装饰器的等价物。为了精简内容,我尝试复制一个简单的Python装饰器:#!/usr/bin/envpythonimportmathdefdocument(f):defwrap(x):print"Iamgoingtosquare",xf(x)returnwrap@documentdefsquare(x):printmath.pow(x,2)square(5)运行这个给我:Iamgoingtosquare525.0因此,我想创建一个函数square(x),但要对其进行装饰,以便它在执行之前提醒我它要对什么进行平方。让我们去掉糖

  5. javascript - 将 Sweet alert 与 Typescript 类一起使用 - 2

    我在typescript类中有一个方法,看起来像这样varconfirmation=confirm("RunAgentJob?");if(confirmation){console.log('yes');}else{console.log('no');}我正在尝试将其转换为使用SweetAlert,因此我将其放入方法中。但是Typescript无法识别它抛出一个Cannotfindnameswalswal("hello");我已经导入了sweetalert如下我做错了什么?如果我尝试在一个普通的*.js文件中使用swal(),它会工作正常。仅当它位于*.ts文件中时。

  6. javascript - 什么是 TypeScript 中的 F 界多态性 - 2

    我注意到TypeScript1.8版本支持F-BoundedPolymorphism.用外行的话来说,它是什么以及它有什么帮助?我假设由于此功能很早就包含在内,所以它一定非常重要。 最佳答案 这基本上意味着您拥有函数引用的泛型列表,并且在该泛型列表中,一种类型可以引用另一种类型,以定义两种泛型类型之间的关系。functionsomeFunction(t:T,u:U):T{returnt;}constdog=someFunction(newDog(),newCat());万岁!现在,有了有界泛型,它们可以相互引用来定义它们之间关系的界

  7. javascript - 如何在 TypeScript 中使用类类型作为映射键? - 2

    我想在反射(reflect)类类型的键下的TypeScript(JavaScript)映射中存储一些信息。这是因为存储的数据实际上是静态的并且适用于每个类型,而不是每个实例。这是我声明Mapatm的方式:privatestaticfollowSetsByATN:Map=newMap();但是,number应该是类类型。如何实现? 最佳答案 如果您有一个对象({})作为映射,则键必须是字符串(或自动转换为字符串的数字)。在这种情况下,您可以使用toString()方法:classA{}console.log(A.toString())

  8. javascript - VS Code 能否在文件重命名/移动时自动更新 JavaScript 和 TypeScript 导入路径? - 2

    是否有用于vscode的模块可以更新文件路径?例如如果我有:import'./someDir/somelib'然后我重命名或移动somelib,它会自动更新所有被引用的文件中的文件路径吗? 最佳答案 此功能是在VSCode1.24(trackingissue)中为JavaScript和TypeScript添加的当您移动或重命名文件时,系统现在会提示您查看是否要更新导入:这由javascript.updateImportsOnFileMove.enabled和typescript.updateImportsOnFileMove.ena

  9. javascript - typescript 无法导入没有扩展名的文件 - 2

    我正在尝试在我的新Angular2项目中导入文件。入口文件“main.ts”能够使用以下方式导入其他typescript文件:import{AppModule}from'./module/app.module';另一方面,“app.module.ts”无法导入没有文件扩展名的ts文件:import{AppComponent}from'../component/app.component';如果我在文件名中添加“.ts”,一切都会按预期进行...我的错误是什么?我假设我正在按照Angular指南(https://angular.io/docs/ts/latest/guide/webpac

  10. javascript - 通过将它们的方法包装在一起,Typescript 类对象的性能是否会变慢? - 2

    我可能是错的,但通过查看typescriptsplayground,我注意到他们将类的方法与对象变量包装在一起,感觉每次我调用新对象时它可能会降低性能。例如类的TypescriptPlayground输出varFatObject=(function(){functionFatObject(thing){this.objectProperty='string';this.anotherProp=thing;}FatObject.prototype.someMassivMethod=function(){//manylinesofcode//...//...//...//.........

随机推荐