Loading

ts-装饰器

装饰器是一种特殊的函数,可以对类、方法、属性、参数进行扩展

装饰器目前是实验性特性,需要手动调整配置开启装饰器

// tsconfig.json文件中,将该行注释打开

"experimentalDecorators": true, 

类装饰器

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

基本使用


// 装饰器会在类定义的时候执行
// target表示被装饰的类
function ClassString(target:Function){
    // 在被装饰的类的原型上添加getInfo方法
    target.prototype.getInfo = function (){
        return "info"
    }

    // 在被装饰的类的原型上添加toString方法
    target.prototype.toString = function (){
        return JSON.stringify(this)
    }
}

@ClassString
class User{
    constructor() {
    }
}

const user = new User()


// toString是 js有的内置方法,可以直接调用
user.toString();


// getInfo是自定义的方法,TypeScript 编译器在静态类型检查时不知道 User 类有 getInfo 方法
// 虽然装饰器在运行时向类的原型添加了这个方法,但 TypeScript 的静态类型系统并不自动识别这种动态添加的方法
// user 对象断言为 any 类型 绕过编译器的检查
(user as any).getInfo()

// 或者可以@ts-ignore忽略对应的代码检查,可以使用
// @ts-ignore
user.getInfo()

类装饰器的返回值

类装饰器有返回值:若类装饰器返回⼀个新的类,那这个新类将替换掉被装饰的类


function  Test(target:Function){
    // 装饰器有返回值时,该返回值会替换掉被装饰的类
    return class {

        constructor() {
            console.log("test")
        }

    }

}

@Test
class  User{
    constructor() {
        console.log("user")
    }

}


// user类会变替换为test类
const user = new User()


构造类型

在 TypeScript 中, Function 类型所表示的范围⼗分⼴泛,包括:普通函数、箭头函数、⽅ 法等等

但并⾮Function 类型的函数都可以被 new 关键字实例化,例如箭头函数是不能 被实例化的

  1. 声明构造类型
// new 表示:该类型是可以⽤new操作符调⽤。
// ...args 表示:构造器可以接受【任意数量】的参数。
// any[] 表示:构造器可以接受【任意类型】的参数。
// {} 表示:返回类型是对象(⾮null、⾮undefined的对象)


// 定义类的类型,涵义是构造类型
type ClassType =  new (...args: any[]) => {};


function  Test(target:ClassType){

}

@Test
class User{}
  1. 声明构造类型、静态属性


type ClassType = {
    new(...args: any[]): {}; // 构造签名
    age: number  // 静态属性
}


// 被装饰的类类型ClassType
function Test(target:ClassType){
    console.log("1")

}


@Test
class  User{
   static age:number
}

 


替换被装饰的类

type Constructor = new (...args:any[]) => {}


// 装饰器 target是Constructor约束的泛型
function  LogTime<T extends  Constructor>(target:T){
    // 新类继承自被装饰的类,将被继承的类替换为新类
    return class  extends target{
        createdTime:Date
        constructor(...args:any[]) {
            super(...args);
            this.createdTime = new Date()
        }

        getTime(){
            return `对象创建时间${this.createdTime}`
        }
    }
}



@LogTime
class User{
    constructor(public a:string,public b:string) {
    }
    
    getInfo(){
        console.log(this.a,this.b)
    }
}


const user = new User("1","2")
// @ts-ignore
console.log(`对象的创建时间${user.getTime()}`)
// 装饰器虽然替换了新类,但是继承了target所以可以继续使用target的方法
user.getInfo()


装饰器工厂

装饰器⼯⼚是⼀个返回装饰器函数的函数,可以为装饰器添加参数,可以更灵活地控制装饰器的⾏为

// 定义一个装饰器工厂,即函数,可以传参,返回一个装饰器
function LogInfo(count: number) {
    // 返回的装饰器,target是被装饰的类
    return function (target: Function) {
        target.prototype.getInfo = function (target: Function) {
            for (let i = 0; i < count; i++){
                console.log(this.a,this.b)
            }
        }
    }
}


@LogInfo(3) // 等同于 LogInfo(3)(User)
class User{
    constructor(public a:string,public b:string) {
    }
}
const user = new User("1","2")

装饰器组合

装饰器可以组合使⽤,执⾏顺序为:先由上到下的执⾏所有的装饰器⼯⼚,依次获取到装饰器,然后再由下到上执⾏所有的装饰器



// 装饰器
function test(t:Function){
    console.log("test")
}

// 装饰器工厂

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

@test
@test1()
@test2
@test3()
class User{

}

// 执行顺序为test3、test2、test1、test
const user = new User()

属性装饰器

基本使用
// target:对静态属性来说值是类,对于实例属性来说值是类的原型对象
// propertyKey:属性名
function Test(target:object,propertyKey:string){
    console.log(target,propertyKey)
    
}


class User{
  // 装饰属性
    @Test age:string

    constructor(age:string) {
        this.age = age
    }
}
const user = new User("1")
属性遮蔽

指内部作用域中的属性定义覆盖了外部作用域中同名属性的现象。这种情况在类(Class)、接口(Interface)和嵌套对象等场景下较为常见

class Parent {
    public property: string = 'Parent Value';
}
class Child extends Parent {
    public property: string = 'Child Value';
    public getParentProperty(): string {
        return super.property;
    }
}
let child = new Child();
console.log(child.property); // 输出: Child Value

/*
Child类中的property属性遮蔽了Parent类中的property属性。
直接访问child.property获取的是Child类自身定义的property的值。
通过super.property可以访问到被遮蔽的Parent类中的property

*/
监视属性案例
function  State(target:object,propertyKey:string){
    // 存储属性的内部值
    let key = `__${propertyKey}`

    // 使用Object.defineProperty替换的类的原始属性,重新定义属性使用其自定义的getter和settr
    Object.defineProperty(target,propertyKey,{
        get(){
            return this[key]
        },
        set(newValue){
            this[key] = newValue
        }
        }
    )


}

class User {
    name:string
    @State age:number
    constructor(name:string,age:number) {
        this.name = name
        this.age = age
    }
}

const u1 = new User("1",18)
const u2 = new User("2",28)

u1.age = 180
u2.age = 280


console.log(u1.age)
console.log(u2.age)

方法装饰器

基本使用

// target: 对于静态⽅法来说值是类,对于实例⽅法来说值是原型对象
// propertyKey:⽅法的名称
//  descriptor: ⽅法的描述对象,PropertyDescriptor参数包含了与对象属性相关的配置信息,如configurable(是否可配置)、    enumerable(是否可枚举)、value(属性的值,对于方法来说是函数体、方法本身)、writable(对于方法来说,这个属性通常不太相关,因为方法主要是被调用而不是被赋值)、get和set(主要用于属性的存取控制,对于方法不太常用,但在某些特殊场景下可能会有作用)
function Test(target: object, propertyKey: string, descriptor: PropertyDescriptor){
    console.log(target)
    console.log(propertyKey)
    console.log(descriptor)

}


class  User{
    constructor(public name:string) {
    }

    // 装饰实例方法
    @Test info(){
        console.log(this.name)
    }
    // 装饰静态方法
    @Test static  hello(){
        console.log(this.name,"hello")
    }

}

const user = new User("a")
逻辑追加&数据验证案例
// 装饰器工场
function Validate(maxValue: number) {
    // 装饰器
    return function (target: object, propertyKey: string, descriptor: PropertyDescriptor) {
        // 保存原始方法
        const original = descriptor.value
        // 替换原始方法
        descriptor.value = function (...args: any[]) {
            // 自定义校验逻辑
            if (args[0] > maxValue) {
                throw new Error(args[0])
            }
            // 如果参数符合要求 调用原始方法
            return original.apply(this, args)

        }
    }
}


function Logger(target: object, propertyKey: string, descriptor: PropertyDescriptor) {
    // 保存原始⽅法
    const original = descriptor.value;
    // 替换原始⽅法
    descriptor.value = function (...args: any[]) {
        console.log("开始执行逻辑")
        const result = original.call(this, ...args)
        console.log("逻辑执行结束")
        return result
    }
}

class  User{
    constructor(public age:number,public name:string) {
    }
    
    @Validate(18)
    age_info(){
        console.log(this.age)
    }
    
    
    @Logger
    name_info(){
        console.log(this.name)
    }
    
}

访问器装饰器


// target:对于实例访问器来说值是所属类的原型对象,对于静态访问器来说值是所属类
//  propertyKey:访问器的名称
//  descriptor: 描述对象
function Test(target: object, propertyKey: string, descriptor: PropertyDescriptor) {
    console.log(target)
    console.log(propertyKey)
    console.log(descriptor)

}

class  User{
    
    @Test
    get  age(){
        return 18
    }
    
    @Test
    static get num(){
        return 10
    }   
}
function  Valid(min:number,max:number){
    return function (target: object, propertyKey: string, descriptor: PropertyDescriptor){
        // 保存原始的 setter ⽅法,以便在后续调⽤中使⽤
        const originalSetter = descriptor.set

        // 重写setter方法,加入范围验证
        descriptor.set = function (value:number){
            if (value <min || value > min){
                throw new  Error(`${value}`)
            }
            //  如果值在范围内,且原始 setter ⽅法存在,则调⽤原始 setter ⽅法
            if (originalSetter){
                originalSetter.call(this,value)
            }
        }
    }

}


class User{
    private  _age:number
    constructor(_age:number) {
        this._age = _age
    }
    
    @Valid(-10,10)
    set age(value:number){
        this._age = value
    }
    
    
    get age(){
        return this._age
    }
}


参数装饰器

//  target:如果修饰的是实例⽅法的参数,target 是类的原型对象,如果修饰的是静态⽅法的参数,target是类。
//  propertyKey:参数所在的⽅法的名称。
//  parameterIndex: 参数在函数参数列表中的索引,从 0 开始。
function  Param(target: object, propertyKey: string, parameterIndex: number){
    
}


class  User{
    constructor(public name:string) {
    }
    
    
    info(@Param age:number){
        console.log(this.name,age)
    }
}
posted @ 2024-11-07 17:01  木子七  阅读(6)  评论(0编辑  收藏  举报