[TypeScript]メソッドデコレータ

class Articke{
    public name:string;
    constructor(name:string){
        this.name = name;
    }
    @logged
    showName(){
        console.log(this.name);
    }
}
const art:Articke = new Articke("あいうえお");
art.showName();
setTimeout(art.showName,1000);

function logged(originalMethod:any,context:any){
    function loggedMethod(this:any,...args:any[]){
        console.log(`${context.name}メソッドスタート`);
        const result = originalMethod.call(this,...args);
        console.log(`${context.name}メソッド終了`);
        return result;
    }
    return loggedMethod;
}


 
出力

showNameメソッドスタート
あいうえお
showNameメソッド終了
showNameメソッドスタート
undefined
showNameメソッド終了

 
thisをインスタンスに束縛してthis.nameがundefinedになるのを回避する

class Articke{
    public name:string;
    constructor(name:string){
        this.name = name;
    }
    @bound
    @logged
    showName(){
        console.log(this.name);
    }
}
const art:Articke = new Articke("あいうえお");
art.showName();
setTimeout(art.showName,1000);

function logged(originalMethod:any,context:any){
    function loggedMethod(this:any,...args:any[]){
        console.log(`${context.name}メソッドスタート`);
        const result = originalMethod.call(this,...args);
        console.log(`${context.name}メソッド終了`);
        return result;
    }
    return loggedMethod;
}

//setTimeout(art.showName,1000);のthis.nameがundefinedになるのを回避
function bound(_originalMethod:any,context:any){
    context.addInitializer(function(this:any){
        this[context.name] = this[context.name].bind(this);
    });
}

 
出力

showNameメソッドスタート
あいうえお
showNameメソッド終了
showNameメソッドスタート
あいうえお
showNameメソッド終了

 
 
型を設定すると…


function logged<This,Args extends any[],Return>(
    originalMethod:(this:This,...args:Args) => Return,
    context:ClassMethodDecoratorContext<This,(this:This,...args:Args) => Return>
){
    function loggedMethod(this:This,...args:Args):Return{
        console.log(`${context.name.toString()} start`);
        const result = originalMethod.call(this,...args);
        console.log(`${context.name.toString()} finish`);
        return result;
    }
    return loggedMethod;
}
function bound<This,Args extends any[],Return>(
    _originalMethod:(this:This,...args:Args) => Return,
    context:ClassMethodDecoratorContext<This,(this:This,...args:Args) => Return>
){
    context.addInitializer(function(this:any){
        this[context.name] = this[context.name].bind(this);
    });
}

class Article{
    protected name:string;
    constructor(name:string){
        this.name = name;
    }
    @bound
    @logged
    showData():void{
        console.log(this.name);
    }
}

const article = new Article("John Smith");
article.showData();
setTimeout(article.showData,1000);

 
出力

showData start
John Smith
showData finish
//1秒待つ
showData start
John Smith
showData finish