Combining Different Types of Errors

Can TypeScript's type system be exploited to provide additional information from a repository to a service in case of errors?

I have a service that needs a port for a repository (Interface that the Repository must implement), but since the service must not know the concrete implementation of the repository, the Interface must also define the errors the service can handle. To achieve this, I am using ts-results.

While defining errors as strings is an option, I wanted to pass more detailed information from the repository to the service in case of an error. So, I attempted to define the Errors as a Union Type of various Error Classes. However, the issue arises when default errors match the signature of the more specific errors.

This leads to a situation where any other error can be passed from the repository to the service (port).

// The Port Definition
export abstract class FriendshipRepositoryPort implements IFriendshipRepository {
    abstract load(
        userIdA: UserId, userIdB: UserId
    ): Promise<Result<IFriendshipAggregate, FriendshipRepositoryErrors>>;

    abstract persist(
        friendship: IFriendshipAggregate
    ): Promise<Result<void, FriendshipRepositoryErrors>>;
}
// repo implementation
async persist(friendship: IFriendshipAggregate): Promise<Result<void, FriendshipRepositoryErrors>> {
        // ... preparing persisting the entities
        try {
            return new Ok(await this._persistenceManager.execute(querySpec));
        } catch (e) {
            console.error(e);
            // FIXME: This should not be possible!
            return new Err(new RuntimeError());
        }
    }
// error definition
export type FriendshipRepositoryErrors = UserNotFoundError
    | DomainRuleViolation
    | DatabaseWriteError
    | DatabaseReadError;

Is there a way to ensure that only instances of the specified classes (or their heirs) can be accepted as error types in the Result?

I have also created a playground to illustrate the issue with a small example.

Answer №1

In my opinion, utilizing discriminated union types is the optimal solution in this scenario. While it may appear complex, it offers a secure and precise method to identify potential errors and specifically pinpoint which errors can be injected into your Result.

For a demonstration of how the compiler operates, you can reference this TypeScript playground link.

class CustomErrorA extends Error{
    #type = CustomErrorA.name;
}
class CustomErrorB extends Error {
    #type = CustomErrorB.name;
}

class UnauthorizedError extends Error {
}

class CustomErrorC extends CustomErrorB {
    #type = CustomErrorC.name;
}

type PermittedErrorTypes =  CustomErrorA | CustomErrorB;

function handleCustomError(error: PermittedErrorTypes): void {
    if(error instanceof CustomErrorA) {
        throw error;
    }
    if(error instanceof CustomErrorB) {
        throw error;
    }
    // No other possibilities remain.
    // Any additional permitted errors will trigger a compiler error
    const exhausted: never = error;
}

handleCustomError(new CustomErrorA());
handleCustomError(new CustomErrorB());
handleCustomError(new UnauthorizedError());
handleCustomError(new CustomErrorC());

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

During the present module, retrieve the runtime list of all modules that are directly imported (Javascript/Typescript)

Imagine you have a set of modules imported in the current module: import {A1, A2, A3} from "./ModuleA"; import {B1, B2, B3} from "./ModuleB"; import {C1, C2, C3} from "./ModuleC"; function retrieveListOfImportedModules() { // ...

Creating various subtypes for graphql-codegen

Currently, I am utilizing the typescript-operations package within the framework of the graphql-codegen library. Previously, I was accustomed to using Apollo's deprecated codegen and appreciated how it exported types seamlessly. For example, let&apos ...

Interface in React Typescript does not include the specified property

Just starting out with React after some previous experience with Angular. I've been trying to create a component that accepts a data model or object as a parameter. Here's what I have: import react from 'react' interface SmbListItem{ ...

Concerning the utilization of the <mat-toolbar> element within Angular

Is the mat-toolbar in Angular going to persist across all components and pages of the application? Will it be present in every component throughout the application? <mat-toolbar color="primary"> <mat-toolbar-row> <span>Welcome to C ...

The latest update of WebStorm in 2016.3 has brought to light an error related to the experimental support for decorators, which may undergo changes in forthcoming

Hello, I recently updated to the latest WebStorm version and encountered this error message: Error:(52, 14) TS1219:Experimental support for decorators is a feature that is subject to change in a future release. Set the 'experimentalDecorators' ...

Oops! Angular2 couldn't find a provider for HttpHandler

I have been working on implementing HttpCache through an interceptor. Below is the code snippet for caching-interceptor.service.ts: import { HttpRequest, HttpResponse, HttpInterceptor, HttpHandler, HttpEvent } from '@angular/common/http' import ...

Pass on only the necessary attributes to the component

I have a simple component that I want to include most, if not all, of the default HTML element props. My idea was to possibly extend React.HTMLAttributes<HTMLElement> and then spread them in the component's attributes. However, the props' ...

Encountering the error message "express.default is not a function" while attempting to start the node server within a container

Whenever I try to start my node server in a remote container, I keep encountering an error stating "express.default is not a function." Can anyone help me figure this out? Here's the content of my main.ts file: import * as express from 'express& ...

How can I use the target type (and maybe even the property type) as a type parameter within a decorator?

In the process of incorporating a deep-watch property decorator in Angular, the following usage has been implemented: @Component({ /* ... */ }) export class AppComp { @Watch( 'a.b.c', function (last, current, firstChange) { // ca ...

Struggling to chart out the post response in Angular 7

I am facing an issue while setting up a service on Angular version 7. The problem arises with the res.json() method, throwing an error stating Property 'json' does not exist on type 'Object'. Below is my service's code: import {In ...

Subscription date is activated when a different part of the state changes in ngrx

Within my state, I have properties named start and end which store dates. Whenever any other part of the state is modified, the subscription for these start and end dates is triggered. Here is the subscription implementation: this.subs.sink = this.store ...

Creating an Object Type from a String Union Type in TypeScript

How can I go about implementing this? type ActionNames = 'init' | 'reset'; type UnionToObj<U> = {/* SOLUTION NEEDED HERE */} type Result = UnionToObj<ActionNames>; // Expected type for Result: `{ init: any, reset: any }` ...

The NullInjectorError has occurred due to the absence of a provider for the InjectionToken angularfire2.app

I am currently working on inserting form data into a Cloud Firestore database. Below is my x.component.ts file where I encountered an error in the constructor section: private firestore: AngularFireStore import { Component, OnInit } from '@angula ...

Extension for VSCode: Retrieve previous and current versions of a file

My current project involves creating a VSCode extension that needs to access the current open file and the same file from the previous git revision/commit. This is essentially what happens when you click the open changes button in vscode. https://i.stack. ...

How to vertically align Material UI ListItemSecondaryAction in a ListItem

I encountered an issue with @material-ui/core while trying to create a ListItem with Action. I am looking for a way to ensure that the ListItemSecondaryAction stays on top like ListItemAvatar when the secondary text becomes longer. Is there any solution to ...

Error: Issue determining the type of variable. Unable to eliminate type 'any'

I am trying to load some widgets from a template object (possibly JSON in the future). Here's an example: type RectangleTemplate = { name: 'Rectangle'; props: { width: number; height: number; } }; type ButtonTemplate = { nam ...

Prevent loading data in Angular 5 by handling errors from undefined objects

Is there a way to avoid console errors from undefined objects? Imagine I have the following code: name : string; constructor(private data: DataService) { this.data.name.subscribe(res => this.name = res); } In my HTML, I have this: <p> {{name}} ...

When evaluating code with eval, properties of undefined cannot be set, but the process works seamlessly without

Currently, I am attempting to utilize the eval() function to dynamically update a variable that must be accessed by path in the format myArray[0][0[1][0].... Strangely enough, when I try this approach, I encounter the following error: Uncaught TypeError: ...

Issues related to the Angular Http module

When attempting to launch my app, I encountered the following error: ERROR Error: StaticInjectorError(AppModule)[ApiUserService -> HttpClient]: StaticInjectorError(Platform: core)[ApiUserService -> HttpClient]: NullInjectorError: No provide ...

After using apt to install tsc, I find myself in a dilemma on how to either delete or upgrade it

After realizing I was missing Typescript on my server, I attempted to run the 'tsc' command. However, I received a message suggesting I use 'apt install tsc' instead. Without much thought, I executed the command. Normally, I would insta ...