typeScript 装饰器decoration

719 阅读3分钟

类装饰器

  • 装饰器本身是一个函数,是在类创建的时候执行,传递给装饰器的函数的参数是构造函数

    function testDecoration2(constructor: any) {
        // 这样可以扩展构造函数的方法
        constructor.prototype.getName = () => {
            console.info('hello');
        }
        console.info('testDecoration2');
    }
    
    function testDecoration2(constructor: any) {
        console.info('testDecoration2');
    }
    
    // 如果存在多个装饰器 下面的装饰器会被先执行
    @testDecoration(true)
    @testDecoration2
    class Test {
        
    }
    
  • 上面的例子装饰器的类型是any,不是很规范

    // 接受一个any类型的数组参数返回一个any,new关键字表示可以实例化为构造函数, T就是一个可以实例化的类或者是构造函数
    function testDecoration< T extends new (...args: any[]) => any>(constructor: T) {
        console.info(constructor);
        // class继承的函数必须是构造函数,这个时候就相当于继承类,会先执行父类的构造函数然后执行子类的,也就是说 这个时候name是xin
        // &emsp;所以这个时候的T也是一样的道理 继承了构造函数
        return class extends constructor{
            name = 'xin';
            getName() {
                return this.name;
            }
        }
    }
    
    @testDecoration
    class Test {
        name: string;
        constructor (name: string) {
            console.info('父类')
            this.name = name;
        }
    }
    
    const test = new Test ('kai');
    // 会报错,TS不知道装饰器已经有了getName 所以会报错 必须As
    console.info((test as any).getName());
    
    
  • 使用工厂方式解决getName报错问题

    function testDecoration() {
        return function < T extends new (...args: any[]) => any>(constructor: T) {
            return class extends constructor{
                name = 'xin';
                getName() {
                    return this.name;
                }
            }
        }
    }
    
    const Test = testDecoration5()(
        class {
        name: string;
        constructor (name: string) {
            this.name = name;
        }
    });
    
    const test= new Test ('kai');
    // 不会报错 test 拿到的是已经装饰过后的类 所以可以识别getName
    console.info(test.getName());
    

方法的装饰器

  • 普通的方法装饰器

    // 普通方法 target对应类的prtotype, key是装饰的方法的名字, descriptor就是函数的属性的控制
    // 静态方法 target对应的是类的构造函数
    function functionDecorator (target: any, key: string, descriptor: PropertyDescriptor ) {
        // 可以被重写
        descriptor.writable = true;
        // 对方法进行变更,getName就变了
        descriptor.value = function () {
            return 'test descriptor';
        }
    }
    
    class Test{
        name: string;
        constructor (name: string) {
            this.name = name;
        }
        // 在类创建的时候 就会对类的方法进行装饰
        @functionDecorator
        getName() {
            return this.name
        }
    }
    
    const test = new Test('paul');
    test.getName = () => {
        return 'test getName';
    }
    // 输出test descriptor
    console.info(test.getName());
    
  • 捕获异常装饰器

    
    function catchError(target:any, keys: string, descriptor: PropertyDescriptor) {
        const fn = descriptor.value;
        descriptor.value = function () {
            try{
                fn()
            } catch(e) {
                console.log('异常 属性不存在');
            }
        }
    }
    const userInfo: any = undefined;
    
    class Test {
        @catchError
        getName() {
            return userInfo.name;
        }
        @catchError
        getAge() {
            return userInfo.age;
        }
    }
    
    const test = new Test();
    test.getName();
    

访问器的装饰器

// getter和setter不能使用同一装饰器
function functionDecorator (target: any, key: string, descriptor: PropertyDescriptor ) {
    descriptor.writable = false;
}

class Test{
    private _name: string;
    constructor (name: string) {
        this._name = name;
    }
    // 在类创建的时候 就会对类的方法进行装饰
    get name () {
        return this._name
    }

    @functionDecorator
    set name(name: string) {
        this._name = name;
    }
}

const test = new Test('paul');
// 会报错,因为不可修改
test.name = 'test';
console.info(test.name);

属性装饰器

// target是class的原型
function propxDecorator (target: any, key: string): any {
    // 修改原型的属性值
    target[key] = 'test desc';
    // 返回的新的 会替换老的descriptor
    const descriptor: PropertyDescriptor = {
        writable: false
    }
    return descriptor;
}

class Test{
    @propxDecorator
    name: string;
    constructor (name: string) {
        this.name = name;
    }
}

const test = new Test('paul');
// 会报错 只读的 不能写
test.name = 'xin';
console.info(test.name);

参数装饰器

// target是class的原型
function paramDecorator (target: any, key: string, paramIndex: number) {
    // 0
   console.info(paramIndex);
}

class Test{
    getName(@paramDecorator name: string) {
        return name;
    }

}

const test = new Test();
console.info(test.getName('11'));

装饰器执行顺序

  • 方法装饰器>类装饰器