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):
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.