In Typescript, object properties make assertions about other properties within the object

I have a specific object that I use to store data received from an API. This object is structured as follows:

class Storage {
   isReady: boolean = false;
   content: object || null = null;
}

I have noticed that when isReady is false, content is null. Conversely, when isReady is true, content is an object. How can I efficiently define the types for this scenario?

One approach could be creating a function from isReady to verify if content is null. While this is a viable option, it may not work well with dynamic objects.

Another alternative is:

const storage = new Storage() as ({isReady: false, content:null} | {isReady: true, content: object});

However, this method can be cumbersome, especially for larger objects.

I am seeking a solution like the following:

class Storage {
   _isReady: boolean = false;
   content: object | null = null;
   get isReady(): typeof this.content is object {
       return this_isReady();
   }

Any suggestions would be greatly appreciated.

EDIT: Just to clarify my question a bit further. I am trying to address this issue:

class Storage {
   isReady: boolean = false;
   content: {property:string} || null = null;
   load = () => {
       this.content = {property: "value"};
       this.isReady = true;
   }
}
let storage = new Storage();
storage.load();
if (storage.isReady) {
    console.log(storage.content.property); // TypeScript raises an error, assuming that storage.content can be null
}

Answer №1

Unfortunately, it is not feasible to achieve this functionality within getters; however, it can be implemented in methods.

interface PopulatedStore {
  info: object
  isPopulated(): true
}

class Storage {
  private _isPopulated: boolean = false
  info: object | null = null

  isPopulated(): this is PopulatedStore {
    return this._isPopulated
  }
}

Answer №2

It seems like your class represents a Store that can store different types of objects. To handle this, I suggest creating a generic abstract class to represent the store and then extending this class for each type of product in the store.

You can check out my approach in this playground link.

abstract class Store<T> {
    private _isLoaded: boolean = false;
    abstract data: T | null;

    get isLoaded(): boolean {
        return this._isLoaded;
    }

    set isLoaded(v: boolean) {
        this._isLoaded = v;
    }


    abstract load(): Promise<void>;
    abstract show(): void;
} 

In this abstract class implementation, the data property is abstract with a type of T or null.

interface IProduct {
    name: string;
    value: number;
    stock: number;
}

The above interface represents an item in the store.

class ProductA extends Store<IProduct> {
    data: IProduct | null = null; 

    async load(): Promise<void> {
        // Loading data from db and setting isLoaded to true upon success
        super.isLoaded = true;
        this.data = {name: 'product', value: 2, stock: 10}
    }

    show() {
        if (super.isLoaded) {
            console.log(this.data);
        } else {
            console.log('null');
        }
    }
}

Through extending the Store class, we can fetch and display data using the methods defined.

This is how I would approach the problem of managing a store. Feel free to correct me if I misunderstood your question.

Answer №3

My approach would be to simplify it like this:

type Storage = {
    isActive: false; 
    info: any; // Might consider changing it to Record<string, unknown>
} | {
    isActive: true;
    info: null;
};

In my opinion, using an 'or' in a type is completely fine as long as it's clear.

Answer №4

The interface can be utilized in various ways. Here's an example:

interface IUser {
    isLoggedIn: boolean;
    details: object | null;
}

class User implements IUser {
   isLoggedIn: boolean = false;
   details: object || null = null;
}

"In TypeScript, interfaces can be used to ensure that a class adheres to a specific contract, similar to how it's done in languages like C# and Java." - for more information, visit: https://www.typescriptlang.org/docs/handbook/interfaces.html#class-types

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 announcement will not be made as a result of the utilization of a private name

I've been working on transitioning a project to TypeScript, and while most of the setup is done, I'm struggling with the final steps. My goal is to use this TypeScript project in a regular JavaScript project, so I understand that I need to genera ...

Node.js server allows for accessing AJAX requests seamlessly

I need to send a parsed AST of JavaScript code to a server for processing, and then receive a list of completions back. However, when I log the AST to the client console before sending it, the structure appears like this: [ { "id":0, "type":"Program", "ch ...

Is Angular UI's data binding more of a push or pull mechanism? How can I optimize its speed?

Suppose I have a variable a that is displayed in HTML as {{a}}. If I then update its value in TypeScript using a = "new value";, how quickly will the new value be reflected in the user interface? Is there a mechanism that periodically checks all bound var ...

What are the steps to sorting in JavaScript?

I need help with sorting an array. The array I have looks like this: var temp = [{"rank":3,"name":"Xan"},{"rank":1,"name":"Man"},{"rank":2,"name":"Han"}] I've tried to sort it using the following code: temp.sort(function(a){ a.rank}) But unfortun ...

PHP variable not receiving Ajax variable

As a beginner in Ajax and jQuery, I am learning through Stack Overflow and online tutorials. Please be considerate of my novice level as you read and potentially offer advice on my post. I have successfully created a form that displays a message upon subm ...

Is there a way to incorporate Vue script in Laravel without utilizing Vue templates?

I have a question that may seem simple, but I'm curious about the best way to use vue script on pages individually without declaring it globally. For example, I have multiple pages in Laravel Blade such as the home page, category page, and product pag ...

What is the main object used in a module in Node.js for global execution?

Node.js operates on the concept of local scope for each module, meaning variables defined within a module do not automatically become global unless explicitly exported. One question that arises is where a variable declared in a module file belongs in term ...

Retrieving output from a JavaScript function

When running the code, the following logs are generated: "generating my graph" myMain.js:110 "Getting credits" myMain.js:149 "debits array is 3.9,4.2,5.7,8.5,11.9,15.2,17,16.6,14.2,10.3,6.6,4.8" myMain.js:169 "Credits data = 10.7,20.5" myMain.js:156 ...

What could be causing the issue with this function not loading correctly the first time in Node.JS?

I am currently in the process of developing a Twitter bot using Node.JS. One issue I am facing is with a function that utilizes an npm library called "scrapejs" to extract data from Yahoo Finance. The problem arises when this function loads after the initi ...

Why is my JavaScript if statement not functioning properly when paired with an else statement

Currently, I am in the process of developing a game and have encountered an issue with implementing a new item for purchase. Despite having all the necessary components in place, the code seems to be skipping over the function unexpectedly. function buy ...

Having difficulty adjusting the width of a div element

I've been struggling to adjust the width of the div assigned with the colors class, whether in percentage or pixels. var colors = ["RED", "GREEN", "BLUE", "YELLOW", "PURPLE"]; for (var h = 0; h <= 4; h++) { for (var i = 0; i <= colors.lengt ...

Styling the pseudo element ::part() on an ion-modal can be customized based on certain conditions

Looking for a solution regarding an ion-modal with specific CSS settings? I previously had the following CSS: ion-modal::part(content) { width: 300px; height: 480px; } Now, I need to adjust the height based on conditions: if A, the height should be lo ...

Having trouble iterating through a grouped array in JavaScript?

Regrettably, I am facing issues once again with my grouped messages. Although I have received a lot of assistance from you previously, I still find myself struggling and hesitant to ask for help again. Initially, my objective was to group messages based o ...

Prettier seems to be producing varied outcomes across various machines

My teammate and I are collaborating on the same project, but we're facing an issue where our Prettier configurations conflict. Each time we push our code to Github, his Prettier format overrides mine. Here's an example of his formatting: const in ...

What is the best method to divide a string, enclose it within a span tag, and generate fresh code using jQuery?

Is there a way to separate multiple links, enclose them in a span tag, and then iterate through them to display them back within the a tag? I have managed to split all the dates into the dateArr array, but I need help looping through them to output each on ...

Determine whether the click occurs inside or outside of a bar on a ChartJS graph

I'm currently working with a bar graph using chartJS. I'm trying to figure out how to detect where the user clicked - whether it was inside or outside of the bar region in chartJS. const waterFChart = new Chart(canvasRef.current, { plugins: [ ...

Unable to retrieve form values from a $modalInstance

When launching a $modalInstance, the user will need to select an option from dynamically loaded radio inputs and return the chosen value. To open the $modalInstance, this function is used: $scope.openAddFriendGroup = function () { var addFriendGroupMod ...

Encountering an error message stating, "Unable to assign value to 'onclick' property"

Currently, I am at a beginner level in Javascript. While working on some exercises, I encountered an issue with the 'onclick' function as mentioned above. Despite going through other queries in this forum, I have not found a solution that works ...

Error encountered: The combination of NextJS and Mongoose is causing a TypeError where it is unable to read properties of undefined, specifically when trying

Versions: Next.js 14.1 React 18 I am currently developing a profile section where users can update their profile information such as username, name, and profile photo. To achieve this, I have implemented a component that contains a form (using shadcn) to ...

How can I ensure that I am only retrieving the first object within a "for loop" in vuejs and returning its value without getting the rest?

Why am I only able to retrieve the value of the first object in my "for loop" and not all three values as intended? var app = new Vue({ el: '#app', data: { myObj: [ {name: 'Hello', age: 10}, {name: ...