Search code examples
typescriptpromisedeferred

Deferred that extends Promise


How to implement a Deferred promise that extends Promise? It's important to extend Promise for type-safe usage where a typical Promise is expected.

Following implementation

export class Deferred<T> extends Promise<T> {                                   
  public _resolveSelf;
  public _rejectSelf;                                                           
  constructor() {
    super(
      (resolve, reject) =>
      {
        this._resolveSelf = resolve
        this._rejectSelf = reject
      }
    )
  }                                                                             
  public resolve(val:T) { this._resolveSelf(val) }
  public reject(reason:any) { this._rejectSelf(reason) }                        
}

throws TypeError: _this is undefined.

In this Typescript playground, one can see that the compiled javascript is funny. In line 15, during declaration of _this already its properties are being assigned.


Solution

  • export class Deferred<T> implements Promise<T> {
    
      private _resolveSelf;
      private _rejectSelf;
      private promise: Promise<T>
    
      constructor() {
        this.promise = new Promise( (resolve, reject) =>
          {
            this._resolveSelf = resolve
            this._rejectSelf = reject
    
          }
        )
      }
    
      public then<TResult1 = T, TResult2 = never>(
        onfulfilled?: ((value: T) =>
          TResult1 | PromiseLike<TResult1>) | undefined | null,
        onrejected?: ((reason: any) =>
          TResult2 | PromiseLike<TResult2>) | undefined | null
        ): Promise<TResult1 | TResult2> {
          return this.promise.then(onfulfilled, onrejected)
        }
    
      public catch<TResult = never>(
        onrejected?: ((reason: any) =>
          TResult | PromiseLike<TResult>) | undefined | null
        ): Promise<T | TResult> {
          return this.promise.catch(onrejected)
        }
    
      public resolve(val:T) { this._resolveSelf(val) }
      public reject(reason:any) { this._rejectSelf(reason) }
    
      [Symbol.toStringTag]: 'Promise'
    
    }
    

    Method signatures of then and catch copy-pasted from Typescript's Promise interface and cosmetically cleaned up. The [Symbol.toStringTag] line is also necessary for Typescript to consider this a Promise, though you only find that out at compile-time.