Over the weekend, I added an Angular 14 front-end to my Strangler function flag exploration in Lucee CFML. Nonetheless, one thing wasn’t sitting proper with me: Error dealing with. In TypeScript, the kind of an error variable inside a catch
block (or Promise
callback) is all the time any
. This makes for comparatively straightforward error dealing with; however, it side-steps the sort security usually offered by the compiler. As such, I wished to return and add a Sort Guard with a Sort Predicate that may assist my error dealing with workflow slim down the worth being caught.
ASIDE: Apparently, as of TypeScript 4, there may be now an
unknown
sort which is getting used for error dealing with. I’ve not personally tried that as I’m a number of years behind on my TypeScript abilities.
In my Angular 14 utility, I’ve an ApiClient
class that proxies the HttpClient
and is answerable for making requests to the API end-point of my Lucee CFML server. On the Lucee / ColdFusion aspect of the community, I centralize my error dealing with and error message translation such that the API will return a message
that’s protected to point out the consumer. This fashion, I haven’t got to recreate the entire error dealing with logic on each the Server and the Consumer.
Now, as a part of the request workflow in my ApiClient
, I intercept error responses and translate them into predicable – and protected – error constructions that may be consumed in the remainder of the appliance. A part of this course of entails plucking the aforementioned consumer pleasant error message out of the Server response and making it accessible to the Angular utility.
Here is the personal technique, normalizeError()
, that takes the error returned by the HttpClient
and makes certain that it has a developer-friendly form:
export class ApiClient {
// ... truncated ....
/**
* I normalize the given error to have a predictable form.
*/
personal normalizeError( errorResponse: any ) : ResponseError {
// Setup the default construction.
// --
// NOTE: The "isApiClientError" property is a flag utilized in different components of the
// utility to facilitate sort guards, sort narrowing, and error consumption.
var error = {
isApiClientError: true,
knowledge: {
sort: "ServerError",
message: "An sudden error occurred whereas processing your request.",
rootCause: null
},
standing: "" )
};
// If the error knowledge is an Object (which it ought to be if the server responded
// with a domain-based error), then it ought to have "sort" and "message"
// properties inside it. That stated, simply because this is not a transport error, it
// doesn't suggest that this error is definitely being returned by our utility.
if (
( errorResponse.error?.strangler === true ) &&
( typeof( errorResponse.error?.sort ) === "string" ) &&
( typeof( errorResponse.error?.message ) === "string" )
) {
error.knowledge.sort = errorResponse.error.sort;
error.knowledge.message = errorResponse.error.message;
// If the error knowledge has some other form, it implies that an sudden error
// occurred on the server (or someplace in transit, reminiscent of on the CDN, Ingress
// Proxy, Load Balancer, and so on). Let's move that uncooked error by way of because the rootCause,
// utilizing the default error construction.
} else {
error.knowledge.rootCause = errorResponse.error;
}
return( error );
}
}
Finally, if the server returns an error response, and the .strangler
flag is ready to true
, it implies that the message
property embedded throughout the error response is protected to point out the consumer. After all, there are many causes why an HTTP request might fail, having nothing to do with my ColdFusion utility’s error dealing with. As such, I’ve to examine the HttpClient
error for stated flag earlier than I try to extract the consumer pleasant error message.
Now, even with this predictable form, the catch
blocks and Promise
handlers nonetheless use any
for the error
object as a result of there is no assure as to the place an error got here from throughout the call-stack. However, throughout the catch
blocks, we will apply “sort narrowing” strategies so as to add runtime predictability.
“Narrowing” is the method by which TypeScript refines a given worth to be of a extra particular sort. So, in a catch
block, we will slim the error
object from the sort any
all the way down to the sort ResponseError
, which is being returned by my ApiClient
. And to do that, we will use a Sort Guard.
A Sort Guard is a operate whose return sort is a Sort Predicate. A sort predicate takes the type of:
parameterName is Sort
And, when this Sort Guard operate returns true
, it tells TypeScript that the Sort Predicate may be utilized and that the worth handed to the given guard operate is assured to be of the given sort. In consequence, TypeScript is ready to slim the error sort from any
all the way down to the sort offered by the Sort Guard.
In my ApiClient
class, I’m offering a member technique that can be utilized as a kind guard – discover that I’m utilizing the isApiClientError
flag being offered by the error-normalization technique above:
export class ApiClient {
// .... truncated ....
/**
* By default, errors in a catch block are of sort "any" as a result of it is unclear the place in
* the callstack the error was thrown. This technique offers a runtime verify that
* ensures that the given error is an API Consumer error. When this technique returns
* "true", TypeScript will slim the error variable to be of sort ResponseError.
*/
public isApiClientError( error: any ) : error is ResponseError {
return( error?.isApiClientError === true );
}
}
Now, inside my error dealing with workflow, I can use this isApiClientError()
sort guard technique earlier than trying to extract the user-friendly error message
offered by my ColdFusion server. On this Angular 14 utility, I am implementing this course of within the getMessage()
technique of my ErrorService
(which overrides the core implementation of Angular’s ErrorHandler
):
export class ErrorService implements ErrorHandler {
personal apiClient;
/**
* I initialize the API consumer with the given dependencies.
*/
constructor( apiClient: ApiClient ) {
this.apiClient = apiClient;
}
// ---
// PUBLIC METHODS.
// ---
/**
* I try to extract the human-friendly error message from the given error. Nonetheless,
* since there aren't any ensures as to the place within the utility this error was thrown,
* we must do some introspection / sort narrowing so as to discover probably the most
* acceptable error message property to show.
*/
public getMessage( error: any ) : string {
// If that is an API Consumer error, the embedded message is trusted and may be
// rendered for the consumer.
if ( this.apiClient.isApiClientError( error ) ) {
return( error.knowledge.message );
}
return( "Sorry, we couldn't course of your request." );
}
/**
* I present a centralized location for logging errors in Angular.
*/
public handleError( error: any ) : void {
// NOTE: Sooner or later, this might ALSO be used to push the errors to a distant log
// aggregation API end-point or service.
console.error( error );
}
}
As you’ll be able to see, within the getMessage()
technique, I am utilizing the isApiClientError()
technique so as to slim down the error sort earlier than entering into the advanced knowledge construction for the human-friendly error message offered by the ColdFusion API.
Now, in my Element-level kind processing and error dealing with, I can catch any error and simply – and safely – hand it off to this getMessage()
technique:
export class CreateViewComponent {
// .... truncated ....
/**
* I submit the brand new function flag for processing.
*/
public createFeatureFlag() : void {
if ( this.isProcessing ) {
return;
}
this.isProcessing = true;
this.errorMessage = null;
this.featureFlagService
.createFeatureFlag(/* ... kind knowledge ... */)
.then(
( response ) => {
this.router.navigate([ "/feature-flag", this.form.key ]);
},
( error ) => {
this.isProcessing = false;
// We're taking the error message, which is at the moment of
// sort `any`, and we're handing it off to the ErrorService,
// which is able to NARROW THE TYPE DOWN to the `ErrorResponse`
// construction returned by the ApiClient. This enables the
// ErrorService to securely extract the user-friendly error
// message returned by the ColdFusion API.
this.errorMessage = this.errorService.getMessage( error );
}
)
;
}
}
Essentially, my Angular utility’s runtime conduct is not any completely different than it was earlier than. However now, I’ve higher error dealing with in place by way of sort guards and sort narrowing. This illustrates the first worth of TypeScript (for me): that it forces me to suppose extra deeply about how my code is being run.
Need to use code from this put up?
Try the license.