Implementing multiple TypeScript classes with the same interface and comparing the properties of their objects

Looking to incorporate 2 classes for business logic within my application. Below is some pseudo code showcasing the use of object and string types to specify each logic:

Includes interface and class declarations;

interface IResult<T, E> {
    result: T;
    err: E;
}

class Fail<E> implements IResult<null, E> {
    private readonly _error: E;

    constructor(error: E) {
        this._error = error;
    }

    get err(): E {
        return this._error;
    }

    get result(): null {
        return null;
    }
}

class Success<T> implements IResult<T, null> {
    private readonly _result: T;

    constructor(result: T) {
        this._result = result;
    }

    get err() {
        return null;
    }

    get result(): T {
        return this._result;
    }
}

In need to acquire one of these instances from a service, for instance DetailsFactory. The response type has been specified where Success should give back object and Fail should provide string:

type Result<T, E> = Success<T> | Fail<E>;

Furthermore, an interface is utilized:

interface IDetailsFactory {
    make(): Result<object, string>;
}
class DetailsFactory implements IDetailsFactory {
    private readonly _type: string;
    private _operation: object;

    constructor(type: string) {
        this._type = type;
    }

    public make() {
        switch (this._type) {
            case '1': this._operation = {type: '1', name: 'First'}; break;
            case '2': this._operation = {type: '2', name: 'Second'}; break;
            case '3': this._operation = {type: '3', name: 'Third'}; break;
            default: return new Fail('Type is not specified');
        }

        return new Success(this._operation);
    }
}

Used like so:

const detailsFactory = new DetailsFactory('1');
const {result, err} = detailsFactory.make();

Upon receiving the expected object in the result field, and null in err, running a check:

if (!err) {
    console.log(result.name);
}

An issue arises with TypeScript showing the error

Error:(96, 14) TS2531: Object is possibly 'null'.
. While I can check result instead of !err, it's not as elegant and I prefer early function exit.

The question remains: how can I inform TypeScript that if there are no errors (!err = true), trust me to access data from the result field without assuming result is potentially null when error = null?

Answer №2

To narrow the types as expected, you can utilize a discriminated union in TypeScript. However, currently TS does not support narrowing one variable based on another; only a single variable can be narrowed at a time.

Furthermore, using additional generics is necessary to forward the actual return type.

type DetailsFactoryResult = 
     | { type: '1', name: string }
     | { type: '2', name: string }
     | { type: '3', name: string }

class DetailsFactory implements IDetailsFactory<DetailsFactoryResult> {
    // ...    
    public make(): Result<DetailsFactoryResult, string> {
        // ...
    }
}

const detailsFactory = new DetailsFactory("1");
const r = detailsFactory.make();

if (r.err == null) {
    console.log(r.result.name);
}

Playground Link

Answer №3

Additionally, I have the ability to incorporate a function in order to validate the outcome within the interface and execute it with type verification.

interface IOutcome<T, E> {
    result: T;
    err: E;
    isPositive(): boolean;
}
class Achievement<T> implements IOutcome<T, null> {
    private readonly _result: T;

    constructor(result: T) {
        this._result = result;
    }

    get err() {
        return null;
    }

    get result(): T {
        return this._result!;
    }

    isSuccessful(): this is Success<NonNullable<T>> {
        return Boolean(this._result);
    }
}

Subsequently, I can utilize it in the following manner

const infoBuilder = new InfoBuilder('1');
const record = infoBuilder.create();

if (data.isSuccessful()) {
    console.log(record.result.name);
}

However, my intention is to find a more graceful solution where I can retrieve data through destructuring and straightforward !err validation similar to what was mentioned in my original query.

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

The arrow function in Jest is missing a name property

Currently, my setup includes: node.js: 9.8.0 Jest: 23.4.2 ts-jest: 23.1.3 typescript: 2.9.2 While attempting the following in my *.test.ts files: const foo = () => 'bar'; console.log(foo.name); // '' foo contains the name pro ...

Creating a Typescript interface that includes keys from another interface

interface A{ a: string; b: string; c: string; // potentially more properties } interface B{ [K in keyof A]: Boolean; } What could be the issue with this code? My goal is to generate a similar structure programmatically: interface B{ ...

Error encountered when trying to import Typescript file using a relative path

When attempting to execute src/index.js, I encountered the following error: Error: Cannot find module './utils/spinner' The import statement in index.js appears as follows: const { startSpinner, stopSpinner } = require('./utils/spinner&apos ...

Expanding Arrays in TypeScript for a particular type

There is a method to extend arrays for any type: declare global { interface Array<T> { remove(elem: T): Array<T>; } } if (!Array.prototype.remove) { Array.prototype.remove = function<T>(this: T[], elem: T): T[] { return thi ...

How can you convert all nodes of a nested JSON tree into class instances in Angular 2 using Typescript?

I have a Leaf class that I want to use to convert all nodes in a JSON response into instances of Leaf. The structure of the JSON response is as follows: JSON Response { "name":"animal", "state":false, "children":[ { "name" ...

Mapping an array of Type T in Typescript using typings

Suppose we have a type T: type T = { type: string, } and we create a function that takes an array of T and returns an object where the keys are the values of each T.type and the values are objects of type T. const toMap = (...args: T[]) => args.red ...

Unable to access attributes of an undefined value (current state is undefined)

After completing a small project, I attempted to deploy it on Vercel. The project runs smoothly without any errors on my local machine. However, when I tried to run it on the server, I encountered the following error: "Cannot read properties of undefined ( ...

The Angular7 counterpart of the C# attribute decorator

I'm working with an API method that has an Authorize attribute to verify permissions. [Authorize(ReadIndexes)] public async Task<IActionResult> GetIndexes () { ... } Is there a similar way in Angular to implement permission checks so the API ...

In my efforts to reset the TypeORM MySQL database upon server shutdown in NestJS, I am exploring different approaches

I am looking for a way to clear all entries in my database when the server shuts down. Can anyone help with this? export class RoomsService { async onApplicationShutdown() { await this.roomService.deleteAll() } async deleteAll(): Promise<Delete ...

Is there a way to retrieve the type of a generic class in JavaScript?

class Alpha { static construct<T extends typeof Alpha>(this: T): InstanceType<T> { const v = new Alpha(); return v as InstanceType<T>; } } class Beta extends Alpha {} const x = Alpha.construct(); // generates Alpha const y = ...

What is the best way to strip out a changing segment of text from a string?

let: string str = "a=<random text> a=pattern:<random text (may be fixed length)> a=<random text>"; In the given string above, let's assume that a= and pattern are constants. It is possible that there may or may not be a ...

When attempting to run npm run build for a Next.js application on an EC2 instance, it unexpectedly terminates on its own

While attempting to deploy my Next.js app on EC2, I encountered an issue where the npm run build command was being automatically killed. Suspecting it may be due to insufficient RAM, I switched to an instance type with 4GB of RAM (t3.medium), but the probl ...

Allow for an optional second parameter in Typescript type definition

Here are two very similar types that I have: import { VariantProps } from "@stitches/core"; export type VariantOption< Component extends { [key: symbol | string]: any }, VariantName extends keyof VariantProps<Component> > = Extra ...

Can we guarantee the uniqueness of a function parameter value during compilation?

I have a set of static identifiers that I want to use to tag function calls. Instead of simply passing the identifiers as arguments, I would like to ensure that each identifier is unique and throws an error if the same identifier is passed more than once: ...

Retrieve predefined values from a TypeScript controller in Stimulus using the default Symfony configurations

Currently, I am delving into the realm of Stimulus using a standard Symfony6 and Encore setup, with the only notable difference being my use of Typescript. As per the guidance in the Stimulus documentation, typed values should be utilized in the following ...

Incorporate a JavaScript script into an Angular 9 application

I have been experiencing issues trying to add a script.js file to angular.json and use it in one component. Adding a script tag directly to my HTML file is not the ideal solution. Can someone suggest an alternative approach or point out what I may be missi ...

Encountering an issue with Angular 12 where a TypeError is being thrown, specifically stating "Cannot read properties of null (reading 'length') at

I encountered an error message while making a http request in my Angular Service. Strangely, this error only occurs after I logout, but it disappears upon logging back in: Below is the code snippet of my authentication Service: import { Injectable } from ...

Transfer Typescript Project to Visual Studio Code

When I first started my project, I used the Typescript HTML Application Template project template. It worked well and set up a project for me. However, now I want to transition to using VSCode. The issue I'm facing is figuring out which switches and c ...

ADAL-Node: Unable to locate tenant groups

When the authority URL is similar to (where the domain name belongs to your tenant), an error occurs: The Get Token request returned an HTTP error: 400 with the server response stating "error description AADSTS90002 Tenant 'organizations' not ...

Troubleshooting: Vue and TypeScript Components Not Communicating

Vue is still fairly new to me and I'm struggling with this issue. Despite following code examples, my implementation doesn't seem to be working correctly. The component I defined looks like this: <template> <div class="row"> ...