Skip to content

TypeScript / JavaScript: How to wrap a Promise object using decorator pattern

I’m attempting to wrap a TypeScript (JavaScript) Promise object with the decorator pattern because I need to add additional logic inside the Promise’s then and catch methods (not shown here).

So far, it looks like this:

export class Decorator {
    protected _promise: Promise<any>;

    constructor(promise: Promise<any>) {
        this._promise = promise;
    }

    then(onFulfilled: any, onRejected: any): Promise<any> {
        return this._promise.then(r => onFulfilled(r), r => onRejected(r))
    };

    catch(onRejected: any): Promise<any> {
        return this._promise.catch(r => onRejected(r));
    }
}

(async () {
    // Note that doSomethingAsynchronously() returns a Promise<void>
    await new Decorator(doSomethingAsynchronously()); // ERROR!!
})();

However, as noted by the “ERROR!!” comment above, I get this build error:

Type of “await” operand must either be a valid promise or must not contain a callable “then” member.

I’ve tried experimenting with extending Promise (hence the class declaration would become export class Decorator<T> extends Promise<T>), but then I have to call super() in the constructor and pass it an executor object, which changes the Decorator class substantially. I’d like to avoid needing an executor, and would like to simply pass the inner promise into the constructor.

How can I successfully await the Decorator class’s wrapped promise?

Answer

I’m really not sure what you’re trying to accomplish and I agree with @jfriend00. Maybe asking what you are really trying to solve maybe you can get a better answer.

Anyway here is a way to do what you want… not sure if this is the best way… but it works…

class Decorator {
    protected _promise: Promise<any>;


    constructor(promise: Promise<any>) {
        this._promise = promise;
    }

    getPromise(onFulfilled?: any, onRejected?: any): Promise<any> {
      return this._promise.then(r => {
        console.log('Decorator:onFulfilled');
        onFulfilled(r);
      })
      .catch(r => {
        console.log('Decorator:onRejected');
        onRejected(r);
      })
    }
}

(async () => {
    // Note that doSomethingAsynchronously() returns a Promise<void>
    const doSomethingAsynchronouslyWithSuccess = () => {
      return new Promise<void>((resolve, reject) => {
        setTimeout(() => resolve(), 1000);
      });
    }
    const doSomethingAsynchronouslyWithFail = () => {
      return new Promise<void>((resolve, reject) => {
        setTimeout(() => reject(), 1000);
      });
    }
    await new Decorator(doSomethingAsynchronouslyWithSuccess()).getPromise();
    await new Decorator(doSomethingAsynchronouslyWithFail()).getPromise();
})();