Skip to content

装饰器

装饰器本身是一个函数 装饰器通过@符号来使用

需要开启tsconfig.json中的experimentalDecorators: true

执行顺序

在TypeScript里,当多个装饰器应用在一个声明上时会进行如下步骤的操作:

由上至下依次对装饰器表达式求值。 求值的结果会被当作函数,由下至上依次调用。

类的装饰器

参数

类装饰器接受的参数是类的构造函数

使用

  • 基本使用
ts
function logDocorator(constructor: any) {
    constructor.prototype.logger = function () {
        // 输出日期
        console.log(this.name)
        console.log(new Date().toLocaleString());
    }
}
@logDocorator
class Person {
    constructor(public name: string) {
    }
}
let person: any = new Person('zhangsan');
person.logger();
  • 装饰器传参

装饰器工厂函数

ts
function logDocorator(log: boolean) {
    return function (constructor: any) {
        constructor.prototype.logger = function () {
            // 输出日期
            console.log(this.name)
            if (log) {
                console.log(new Date().toLocaleString());
            }
        }
    }
}
@logDocorator(false)
class Person {
    constructor(public name: string) {
    }
}
let person: any = new Person('zhangsan');
person.logger();

ts类型报错修复

ts
// 1.构造函数使用泛型
function logDocorator<T extends new (...args: any[]) => any>(log: boolean) {
    return function (constructor: T) {
        return class extends constructor {
            name = `new name`
            logger() {
                // 输出日期
                console.log(this.name)
                if (log) {
                    console.log(new Date().toLocaleString());
                }
            }
        }
    }
}

// 2.class使用装饰器修饰过的class
const Person = logDocorator(true)(class  {
    constructor(public name: string) {
    }
})

let person = new Person('zhangsan');
person.logger()

方法装饰器

参数

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

使用

  • 修改descriptor
ts
descriptor.writable = writable
  • 修改原始函数
ts
descriptor.value = function () {
    console.log(new Date().toLocaleString())
}
code
ts
/**
 * @param value
 */
function functionDecorator(value: boolean) {
    /**
     * @param target 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
     * @param key 成员的名字
     * @param descriptor 成员的属性描述符
     */
    return function (target: any, key: string, descriptor: PropertyDescriptor) {
        console.log(target, key, descriptor)
        // 1.修改descriptor
        descriptor.writable = value
        // 2.修改原始函数
        descriptor.value = function () {
            console.log(new Date().toLocaleString())
        }
    }
}
class Person {
    constructor(public name: string) {
    }
    @functionDecorator(false)
    say() {
        console.log(this.name)
    }
}

let person = new Person('zhangsan');
// 此时代码报错,无法修改
person.say = function () {
    console.log(new Date().toLocaleString())
}
person.say()

访问器装饰器

访问器装饰器是 TypeScript 中一种特殊的装饰器,用于装饰类的属性访问器(getter 和 setter)。

它允许你在访问器被定义时对其进行修饰,以实现各种功能,比如添加日志、权限检查等。

参数同方法装饰器

code
ts

type SetterFunction<T> = (this: T, value?: any) => void;

function visitDecorator() {
    /**
     * @param target 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
     * @param key 成员的名字
     * @param descriptor 成员的属性描述符
     */
    return function (target: any, key: string, descriptor: PropertyDescriptor) {
        console.log(target, key, descriptor);
        /**
         * descriptor
         * {
         "enumerable": false,
         "configurable": true
         }
         */
        
        const originalGetter: SetterFunction<any> | undefined = descriptor.get;
        const originalSetter: SetterFunction<any> | undefined = descriptor.set;

        // 重写getter方法
        descriptor.get = function () {
            console.log(`Getting value of property ${key}`);
            return originalGetter?.call(this); // 保留原始getter方法的行为
        };

        // 重写setter方法
        descriptor.set = function <T>(value: T) {
            console.log(`Setting value of property ${key} to ${value}`);
            originalSetter?.call(this, value); // 保留原始setter方法的行为
        };
    };
}

class Person {
    private _name: string;
    constructor(name: string) {
        this._name = name;
    }
    get name() {
        return this._name;
    }
    @visitDecorator()
    set name(name: string) {
        this._name = name;
    }
}

const person = new Person('zhangsan');
console.log(person.name); // 输出: zhangsan
person.name = 'new name'; // 设置成功
console.log(person.name); // 输出: new name

注意

在访问器装饰器中,descriptor 参数是一个包含 get 和 set 方法的对象。虽然 TypeScript 允许你访问 descriptor.writable 属性,但这个属性只在数据属性(即普通的成员属性,而非访问器属性)中有效,对于访问器属性来说,并没有 writable 这个属性。

属性装饰器

参数

target 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。

key 属性名

使用

ts

function nameDecorator() {
    /**
     * @param target 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
     * @param key 成员的名字
     */
    return function (target: any, key: string): any {
        console.log(target, key);
        // 替换原有的属性描述符
        const descriptor: PropertyDescriptor = {
            writable: false,
        }
        return descriptor
    };
}

class Person {
    @nameDecorator()
    public name = 'www'
}

const person = new Person();
console.log(person.name); // 输出: zhangsan
// 报错,不可修改 Uncaught (in promise) TypeError: Cannot assign to read only property 'name' of object '#<Person>'
person.name = 'new name'; // 设置成功
console.log(person.name); // 输出: new name

参数装饰器

参数

target 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。 key 方法名 paramIndex 参数的索引

使用

ts

function paramDecorator() {
    /**
     * @param target 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
     * @param key 成员的名字
     * @param paramIndex 参数的索引
     */
    return function (target: any, key: string, paramIndex: number): any {
        console.log(target, key, paramIndex);
    };
}

class Person {
   getInfo(name: string, @paramDecorator() age: number) {
       console.log(name, age)
   }
}

const person = new Person();
person.getInfo('www', 18)

装饰器的应用

异常处理

ts
function exceptionDecorator(name: string) {
    return function (target: any, key: string, descriptor: PropertyDescriptor) {
        const originalMethod = descriptor.value;
        descriptor.value = function (...args: any[]) {
            try {
                originalMethod.apply(this, args);
                console.log(`${name} is success`);
                return;
            }
            catch (error) {
                console.log(`${name} is error`, error);
            }
        }
    }
}

class Person {
    @exceptionDecorator('getInfo')
    getInfo(name: string, age: number) {
    }
    @exceptionDecorator('getName')
    getName(name: string) {
        throw new Error('null');
    }
}
const person = new Person();
person.getInfo('www', 18)
person.getName('www')