Marius Schulz
Marius Schulz
Front End Engineer

Using Promise.prototype.finally() in TypeScript

ES2018 introduced a new Promise.prototype.finally() method to the standard library. The finally() method lets you execute a callback function once the promise that it's called on has been settled (that is, it has been either fulfilled or rejected and is therefore no longer pending):

somePromise.finally(() => {
  // Code
});

The main benefit of using finally() over then() with two callbacks is that you don't have to duplicate code in both the fulfillment and the rejection handler:

somePromise.then(
  result => {
    // Code
    return result;
  },
  error => {
    // Code
    throw error;
  },
);

Check out the ES2018: Promise.prototype.finally() post by Axel Rauschmayer for more practical use cases.

#Using the Promise.prototype.finally() Method in TypeScript

The TypeScript compiler doesn't have to do anything special to compile calls to the finally() method. However, it might issue a type error like this:

// error TS2339: Property 'finally' does not exist
// on type 'Promise<Response>'.
somePromise.finally(() => {
  // ...
});

If you see this error, the compiler is telling you that it doesn't know anything about a finally() method on the Promise type. In that case, the JavaScript version you're targeting (typically ES5 or ES2015) doesn't define a finally() method on the Promise prototype.

The solution to make the type error go away is to assure the TypeScript compiler that at run-time, the finally() method is going to be available (either natively or via a polyfill). Head over to your project's tsconfig.json file and add the value "es2018.promise" to the "lib" array:

{
  "compilerOptions": {
    // ...
    "lib": ["dom", "es2015", "es2018.promise"]
  }
}

This will include the es2018.promise.d.ts type declaration file in the compilation which defines the finally() method on the Promise interface:

/**
 * Represents the completion of an asynchronous operation
 */
interface Promise<T> {
  /**
   * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The
   * resolved value cannot be modified from the callback.
   * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).
   * @returns A Promise for the completion of the callback.
   */
  finally(onfinally?: (() => void) | undefined | null): Promise<T>;
}

With this type declaration in place, the TypeScript compiler should no longer report a type error when you use the finally() method:

somePromise.finally(() => {
  // Code
});

It's now your responsibility to make sure that the finally() method is actually available in all of your target browsers, either natively or via a polyfill.

#Browser Support

The browser support for the finally() method is quite good (see caniuse.com) — all major browsers implement it natively (screenshot taken on July 23, 2019):

Browser support for the Promise.prototype.finally() method from caniuse.com

If you need to support a browser that doesn't implement the finally() method natively, make sure to include a polyfill in your application (e.g. via the promise.prototype.finally npm package). Note that if Promise itself isn't supported in any of your target browsers, you'll need to polyfill that separately.