2

I am trying to write a Typescript API service. For the service I need a way to check that the method exists when a function such as get is called.

I realise I can do like

get(endpoint: string) {
    this.handleRequest();
}

post(endpoint: string, data: any) {
    this.handleRequest();
}

But I don't particularly want to do that are the top of every method so I didn't know if there was a way to listen within the constructor of the Typescript class for a call of a child function.

It seems a little far fetched to be able to do this but it would be extremely useful in cases like mine so I don't have to keep on doing it.

export class ApiService {
    base_url: string = 'https://jsonplaceholder.typicode.com/posts';
    methods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'];

    /**
     * Create an instance of ApiService.
     * @param {string} base_url
     */
    constructor(base_url: string = null) {
        this.base_url = base_url ? base_url : this.base_url;
    }

    get(endpoint: string): string {
        // duplicated line
        this.handleRequest();

        return 'get method';
    }

    post(endpoint: string, data: any): string {
        // duplicated line
        this.handleRequest();

        return 'post method';
    }

    protected handleRequest(): string {
        return 'handle the request';
    }
}
  • Do it inside `handleRequest`? – Bergi Mar 05 '18 at 21:25
  • Which method (or function) does or does not exist? How do you want to check it? Can you please post the complete code with all the duplication of doing the checks everywhere? – Bergi Mar 05 '18 at 21:27
  • How do you mean? I want to be able to call `api.get('endpoint');` which will then return the values but whenever `api.get('endpoint')` or `api.post('endpoint', ['name' => 'tallent']);` to both run the `this.handleRequest();`'but I don't want to have to duplicate `this.handleRequest();` for all of the methods. –  Mar 05 '18 at 21:28
  • I've updated the question to contain the complete code –  Mar 05 '18 at 21:30
  • Would decorators be a viable solution? You'd still have to attach the decorator to every method. – y2bd Mar 05 '18 at 21:32
  • @y2bd can you do decorators on a method by method basis? –  Mar 05 '18 at 21:34
  • Does handleRequest have to be called for all methods in the class ? Most of them ? Or just a few ? – Titian Cernicova-Dragomir Mar 05 '18 at 21:35
  • @TitianCernicova-Dragomir It'll be called in all of the methods –  Mar 05 '18 at 21:35
  • All except al least `handleRequest` – Titian Cernicova-Dragomir Mar 05 '18 at 21:36
  • @tallent123 See https://www.typescriptlang.org/docs/handbook/decorators.html#method-decorators – y2bd Mar 05 '18 at 21:37
  • @TitianCernicova-Dragomir yeah, all other than `handleRequest` itself. I didn't know if it was possible to just literally listen for a function call from the constructor. Been Googling but no luck. –  Mar 05 '18 at 21:37

1 Answers1

5

You can do this using a decorator that will override all methods of the class with a method that calls the original implementation and your extra method:

function handleRequest() {
    return function<TFunction extends Function>(target: TFunction){
        for(let prop of Object.getOwnPropertyNames(target.prototype)){
            if (prop === 'handleRequest') continue;
            // Save the original function 
            let oldFunc: Function = target.prototype[prop];
            if(oldFunc instanceof Function) {
                target.prototype[prop] = function(){
                    this['handleRequest'](); // call the extra method
                    return oldFunc.apply(this, arguments); // call the original and return any result
                }
            }
        }
    }
}

@handleRequest()
export class ApiService {
    base_url: string = 'https://jsonplaceholder.typicode.com/posts';
    methods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'];

    /**
     * Create an instance of ApiService.
     * @param {string} base_url
     */
    constructor(base_url: string = null) {
        this.base_url = base_url ? base_url : this.base_url;
    }

    get(endpoint: string): string {
        return 'get method';
    }

    post(endpoint: string, data: any): string {
        return 'post method';
    }

    protected handleRequest(): void {
        console.log('handle the request');
    }
}

let s = new ApiService();
s.get("");
s.post("", null);
Titian Cernicova-Dragomir
  • 230,986
  • 31
  • 415
  • 357