当前位置: 首页 > news >正文

TypeScript装饰器

TypeScript装饰器

简介

  1. 装饰器本质是一种特殊的函数,它可以对:类、属性、方法、参数进行扩展,同时能让代码更简洁。
  2. 装饰器自2025年在ECMAScript-6中被提出,已将近10年。
  3. 截止目前,装饰器依然是实验性特性,需要开发者手动调整配置,来开启装饰器支持。
  4. 装饰器有5种:
    • 类装饰器
    • 属性装饰器
    • 方法装饰器
    • 访问器装饰器
    • 参数装饰器

备注:虽然TypeScript5.0中可以直接使用类装饰器,但为了确保其他装饰器可用,现阶段使用时,仍建议使用experimentalDecorators配置开启装饰器支持,而且不排除下一版本中,官方会进一步调整装饰器的相关语法。

类装饰器

基本语法

类装饰器是一个应用在类声明上的函数,可以为类添加额外的功能,或添加额外的逻辑。

首先在tsconfig.json启用实验性装饰器特性

function Demo(target: Function) {
    console.log(target)
}
@Demo
class Person { }

应用举例

定义一个装饰器,实现Animal实例调用toString时返回JSON.stringify的执行结果。

function ToString(targe: Function) {
    targe.prototype.toString = function () {
        return JSON.stringify(this)
    }
    // 禁止添加新属性,现有属性可修改,但是不能删除
    Object.seal(targe)
}

@ToString
class Animal {
    constructor(
        public age: number,
        public name: string,
    ) { }
}
const cat = new Animal(1, "cat")
console.log(cat.toString()) // {"age":1,"name":"cat"}

关于返回值

类装饰器返回值:若类装饰器返回一个新的类,那么这个新类将替换被装饰的类。
类装饰器返回值:若类装饰器无返回值或者undefined,那被装饰的类不会被替换。

function ReturnClass(target: Function) {
    return class {
        test() {
            console.log(100)
            console.log(200)
            console.log(300)
        }
    }
}
@ReturnClass
class Person {
    test() {
        console.log(400)
    }
}
console.log(Person)
// class {
//     test() {
//       console.log(100);
//       console.log(200);
//       console.log(300);
//     }
//   }

关于构造类型

在TypeScript中,Function类型所表示的范围十分广泛,包括:普通函数、箭头函数、方法等等。
但并非Function类型的函数都可以被new关键字实例化,例如箭头函数是不能被实例化的,那么TypeScript中该如何声明一个构造函数呢?有以下两种方式:

  • 仅声明构造类型
type Constructor = new (...arg:any[]) =>{}

new:该类型是可以用new操作符调用。
...args:构造器可以接受【任意数量】的参数。
any[]:构造器可以接受【任意类型】的参数。
{}:返回值是对象(非null、非undefined的对象)。

  • 声明构造类型 + 指定静态属性
// 定义一个构造类型,且包含一个静态属性 wife
type Constructor = {
    // 构造签名
    new(...args: any[]): {}
    // wife 属性
    wife: string,
};
function test(fn: Constructor) {

}
class Person {
    static wife = "alex"
}
test(Person)

替换被装饰的类

对于高级一些的装饰类,不仅仅是覆盖一个原型上的方法,还要有更多功能,例如添加新的方法和状态。

需求:设计一个LogTime装饰器,可以给实例添加一个属性,用于记录实例对象的创建时间,再添加一个方法用于读取创建时间。

type Constructor = new (...args: any[]) => {}
interface Person {
    getTime(): void
}
function LogTime<T extends Constructor>(target: T) {
    return class extends target {
        createdTime: Date
        constructor(...args: any) {
            super(...args)
            this.createdTime = new Date()
        }
        getTime() {
            console.log(this.createdTime)
        }
    }
}

@LogTime
class Person {
    constructor(
        public name: string,
        public age: number,
    ) { }
}

const p1 = new Person("Alex", 12)
p1.getTime()

装饰器工厂

装饰器工厂是一个返回装饰器函数,可以为装饰器添加参数,可以更灵活地控制装饰器的行为。

需求:定义一个LogInfo 类装饰器工厂,实现Person实例可以调用到introduce方法,且introduce中输出内容的次数,由LogInfo接收的参数决定。

interface Person {
    introduce: () => void
}

// 定义一个装饰器工厂LogInfo,它接受一个参数n,返回一个类装饰器
function LogInfo(n: number) {
    // 装饰器函数,target 是被装饰的类
    return function (target: Function) {
        target.prototype.introduce = function () {
            for (let i = 0; i < n; i++) {
                console.log(this.name)
            }
        }
    }
}
@LogInfo(2)
class Person {
    constructor(
        public name: string,
        public age: number
    ) { }
    speak() {
        console.log("hello")
    }
}

const p = new Person("alex", 11);
p.introduce()

装饰器组合

装饰器可以组合使用,执行顺序为:先【由上到下】的执行所有的装饰器工厂,依次获取到装饰器,然后获取到装饰器,然后再【由下到上】执行所有的装饰器。

// 装饰器
function test1(target: Function) {
    console.log("test1")
}

// 装饰器工厂
function test2() {
    console.log("test2工厂")
    return function (target: Function) {
        console.log("test2")
    }
}

// 装饰器工厂
function test3() {
    console.log("test3工厂")
    return function (target: Function) {
        console.log("test3")
    }
}

// 装饰器
function test4(target: Function) {
    console.log("test4")
}
@test1
@test2()
@test3()
@test4
class Person {
    constructor(
        public name: string,
        public age: number
    ) { }
}

const p = new Person("Alex", 12)
// test2工厂
// test3工厂
// test4
// test3
// test2
// test1

属性装饰器

基本语法

function Print(target: object, propertyKey: string) {
    console.log(target, propertyKey)
}

class Person {
    @Print
    name: string
    @Print
    age: number
    @Print
    static grender:string
    constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }
}
const p = new Person("Alex", 11)
  • target:对于静态属性来说是类,对于实例属性来说是类的原型对象。
  • propertyKey:属性名。
    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

属性遮蔽

如下代码:当构造器中的this.age = age试图在实例上复制时,实际上调用了原型上age属性的set方法

class Person {
    name: string
    age: number
    constructor(
        name: string,
        age: number
    ) {
        console.log("触发了")
        this.name = name
        this.age = age;
    }
}
let value = 99
Object.defineProperty(Person.prototype, "age", {
    get() {
        return value
    },
    set(val) {
        value = val
    }
})
const p1 = new Person("Tom", 10)

console.log(p1, "p1")

应用举例

定义一个State装饰器,依赖监听属性的修改

function State(target: object, propertyKey: string) {
    let key = `__${propertyKey}`
    Object.defineProperty(target, propertyKey, {
        get() {
            console.log("get ==>", this[key])
            return this[key]
        },
        set(val) {
            console.log("set ==>", val)
            this[key] = val
        }
    })
}

class Person {
    name: string
    @State
    age: number
    constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }
}
const p1 = new Person("alex", 12)
p1.age = 13

方法装饰器

基本语法

function Demo(target:object,propertyKey:string,descriptor:PropertyDescriptor){
    console.log(target)
    console.log(propertyKey)
    console.log(desc)
}

class Person{
    constructor(
        public name:string,
        public age:number
    ){}
    @Demo speak(){}
    @Demo static print(n:number){
        return n > 16
    }
}
  • target:对于静态方法来说值是类,对于实例方法来说值是原型对象。
  • property:方法的名称。
  • descriptor:方法的描述对象,其中Value属性是被装饰的方法。

应用举例

需求:

  • 定义一个Logger方法装饰器,用于方法执行前和执行后,均追加一些额外逻辑。
  • 定义一个Validate方法装饰器,用于验证数据。
function Logger(target: object, propertyKey: string, descriptor: PropertyDescriptor) {
    const originnal = descriptor.value;
    descriptor.value = function (...args: []) {
        console.log("propertyKey 开始执行")
        const res = originnal.call(this, ...args)
        console.log("propertyKey 执行结束")
        return res;
    }

}


function Validate(max: number) {

    return function (target: object, propertyKey: string, descriptor: PropertyDescriptor) {
        const originnal = descriptor.value;
        descriptor.value = function (...args: any[]) {
            if (args[0] > max) {
                throw new Error("你年龄非法")
            }
            return originnal.apply(this, args)
        }
    }
}

class Person {
    constructor(
        public name: string,
        public age: number
    ) { }
    @Logger speak() {
        console.log(`我叫:${this.name},今年${this.age}`)
    }
    @Validate(10) setAge(age: number) {
        console.log(age)
        this.age = age
    }
}
const p = new Person("Alex", 12)
p.speak()
p.setAge(9)
console.log(p)

访问器装饰器

基本使用

function Demo(target: object, propertyKey: string, descriptor: PropertyDescriptor) {
    console.log(target)
    console.log(propertyKey)
    console.log(descriptor)
}
class Preson {
    @Demo
    get address() {
        return "云南省-昆明市"
    }
    @Demo
    static get country() {

        return "China"
    }
}
  • target:对于静态方法来说值是类,对于实例方法来说值是原型对象。
  • property:方法的名称。
  • descriptor:方法的描述对象,其中Value属性是被装饰的方法。

应用举例

需求:对Weather类的temp属性的set访问器进行限制,设置最低温度-50,最高温度 50

function RangeValidate(min: number, max: number) {

    return function (target: object, propertyKey: string, descriptor: PropertyDescriptor) {
        const origin = descriptor.set;
        descriptor.set = function (value: number) {
            if (value < min || value > max) {
                throw new Error(`${propertyKey}的值应该在${min}${max}之间`)
            }
            if (origin) {
                origin.call(this, value)
            }
        }
    }
}

class Weather {
    private _temp: number;
    constructor(_temp: number) {
        this._temp = _temp
    }
    get temp() {

        return this._temp
    }
    @RangeValidate(-50, 50)
    set temp(value) {
        this._temp = value
    }
}
const w = new Weather(10)
console.log(w.temp)
w.temp = 100

参数装饰器

基本语法

function Demo(target:object,propertyKey:string,paramterIndex:number){

}

class Person {
    constructor(public name:string){}
    speak(@Demo a:any,b:any){
    }
} 
  • target:
    • 如果修饰的是【实例方法】的参数,target 是类的【原型对象】
    • 如果修饰的是【静态方法】的参数,target 是【类】
  • property:参数所在方法的名称。
  • descriptor:参数在函数参数列表中的索引,从0开始。

相关文章:

  • An effective algorithm for peptide de novo sequencing from MS/MS spectra
  • 二分算法刷题
  • 【NLP】 4. NLP项目流程与上下文窗口大小参数的影响
  • llama-factory笔记
  • python二级复习(1)
  • 编程题-第k个语法符号(中等)
  • 在react当中利用IntersectionObserve实现下拉加载数据
  • 使用生成对抗网络(GAN)进行人脸老化生成的Python示例
  • Conda 虚拟环境创建:加不加 Python 版本的深度剖析
  • Python(最新版)集成开发环境PyCharm下载安装详细教程
  • 开源WAF雷池本地化部署与远程查看网站安全防护的详细操作指南
  • 深度解析螺栓连接计算题:原理、计算与应用
  • Helm 简介与安装
  • 【leetcode hot 100 199】二叉树的右视图
  • ModelScope推理QwQ32B
  • SpringBoot手动注册定时任务
  • 【WRF-Urban】使用 CGLC-MODIS-LCZ_100m 数据集运行 WRF 时的城市参数化问题
  • 从0开始搭建微服务架构特别篇SpringCloud网关聚合knife4j
  • S/4 ERP QM 结合 EWM
  • 【C#】Http请求设置接收不安全的证书
  • 营销网站建设联系方式/seo教程 百度网盘
  • 建设部网站一级开发资质/信息流优化师培训
  • 自己做图片的网站/长沙网站提升排名
  • 当下网站建设/百度营消 营销推广
  • 安徽集团网站建设/上海百度整站优化服务
  • 视频网站怎么建设/个人如何优化网站有哪些方法