AngularJS1.x系の$resource型問題を素敵に解消する

typescript TypeScript
angular-icon AngularJS

$resource型問題

素のjavascriptで書いてる場合ならこの対応で問題無いが、

var notes = $resource('/notes/:id', null, {
  'update': { method:'PUT' }
});

TypeScriptの場合はng.resource.IResourceClass<T>型の制約でupdateなんてメソッド無いぞボケって怒られてしまうよね。

this.getResource('publish').update({},
    project,
    ()=> {
        success()
    },
    (e)=> {
        error ? error() : null
    }
)

するとこのようにanyへの変換を強いられ型って一体なんだっけ?という気持ちになる。

(this.getResource('delete') as any).update({},
    project,
    ()=> {
        success()
    },
    (e)=> {
        error ? error() : null
    }
)

解決方法

この問題を解消するためにresourceクラスの基底クラスにng.resource.IResourceClass<T>型を拡張して返すFactoryメソッドを定義するというアプローチを取りました。
(といいつつ内部でanyへの変換をしています。。。いい方法あるかな?)

resource.ts
// extendsしてupdateメソッドを定義する
export interface IResourceClass<T> extends ng.resource.IResourceClass<T> {
    update: angular.resource.IResourceMethod<T>;
}

export default class Resource<T> {
    constructor(protected resource: ng.resource.IResourceService,
                protected path: string) {
    }

    protected getResource(query?: string): IResourceClass<T> {
        // 結局ここはany変換してしまった。。。
        return <any>this.resource(`/api/${this.path}` + (query ? `/${query}` : ''), null, {
            'update': { method:'PUT' }
        })
    }
}

使い方

user.ts
export default class UserResource extends Resource<User> {
    static $inject = ['$resource']
    constructor(protected resource: ng.resource.IResourceService) {
        super(resource, 'users')
    }

    // 'GET /api/users/:userId'
    get(userId: string, success: (user: User)=>void, error?: ()=> void) {
        let res = this.getResource(':userId').get({
                userId: userId
            },
            ()=> {
                success(res)
            },
            (e)=> {
                error ? error() : null
            }
        )
    }

    // 'GET /api/users'
    list(success: (users: Array<User>)=> void, error?: ()=> void) {
        let res = this.getResource().query({},
            ()=> {
                success(res)
            },
            (e)=> {
                error ? error() : null
            }
        )
    }

    // GET /api/users/search'
    search(condition: User, success: (users: Array<User>)=> void, error?: ()=> void) {
        this.getResource('search').query(
            condition,
            ()=> {
                success()
            },
            (e)=> {
                error ? error() : null
            }
        )
    }

    // POST /api/users'
    create(user: User, success: (user: User)=> void, error?: ()=> void) {
        this.getResource().save({},
            user,
            ()=> {
                success(user)
            },
            ()=> {
                error ? error() : null
        })
    }

    // PUT /api/users'
    save(id: number,user: User, success: ()=> void, error?: ()=> void) {
        this.getResource().update({
                id: id
            },
            user,
            ()=> {
                success()
            },
            ()=> {
                error ? error() : null
            })
    }

    // DELETE /api/users'
    delete(user: User, success: ()=> void, error?: ()=> void) {
        this.getResource().delete({},
            user,
            ()=> {
                success()
            },
            (e)=> {
                error ? error() : null
            }
        )
    }
}

あまりTypeScript版のサンプルが落ちていないのと、日本語情報を見かけないのでメモをば。