Limitations on quantity utilizing typescript

Looking to create a type/interface with generics that has two properties:

interface Response<T> {
  status: number;
  data: T | undefined;
}

Specifically, I want to enforce a rule where if the status is not equal to 200, then data must be undefined. On the other hand, if the status is equal to 200, then data must be of type T. This would eliminate the need to always check if response.data is defined after verifying that response.status is 200:

if (response.status === 200 && response.data) {
  // It would be ideal for TypeScript to automatically understand that response.data 
  // is not undefined without additional explicit checks
}

This is what I currently have:

interface IOkResponse<T> {
  status: 200;
  data: T;
}

interface IErrorResponse {
  status: number;
  data: undefined;
}

type Response<T> = IOkResponse<T> | IErrorResponse;

The issue lies in the fact that IErrorResponse does not restrict the status value to numbers other than 200. How can this restriction be implemented?

Answer №1

I concur with @jcalz's assessment that there is no straightforward way to achieve this. One approach you can take is utilizing typeguards. While it enhances the type safety of your code, there is a cost associated with it in terms of function overhead.


interface IOkResponse<T> {
  status: 200;
  data: T;
}

interface IErrorResponse {
  status: number;
  data: undefined
}

type Result<T> = IOkResponse<T> | IErrorResponse;

const isOK = <T,>(arg: Result<T>): arg is IOkResponse<T> => arg.status === 200

const foo = <T,>(arg: Result<T>) => {
  if (isOK(arg)) {
    const y = arg //  IOkResponse<T> 
  } else {
    const z = arg // IErrorResponse;
  }
}

Answer №2

class NewResponse<T> {
    statusCode: number = null;
    get responseData(): T {
        return this.statusCode === 200 && this.responseData || undefined;
    }
}

What do you think about this approach? The data will remain undefined if the status code is equal to 200.

Answer №3

Even though @captain-yossarin provided a more general solution that I accepted as the answer, I wanted to share the approach I ultimately decided on...

export enum StatusCode {
  OK = 200,
  NoContent = 204,
  NotModified = 304,
  BadRequest = 400,
  Unauthorized = 401,
  // Any other important status codes for the client can be added here
}

interface IOkResponse<T> {
  statusCode: StatusCode.OK;
  data: T;
}

interface IErrorResponse {
  statusCode: Exclude<StatusCode, StatusCode.OK>;
  data: undefined;
}

export type ApiResponse<T> = IOkResponse<T> | IErrorResponse;

The function responsible for instantiating my ApiResponse<T> object is working well because TypeScript automatically converts number to StatusCode.

async function parseResponse<T>(response: Response): Promise<ApiResponse<T>> {
  const text = await response.text();
  const statusCode = response.status;
  if (statusCode === StatusCode.OK) {
    const data = JSON.parse(text) as T;
    return { statusCode, data };
  }
  return { statusCode, data: undefined };
}

Now, using my ApiResponse<T> object does not require unnecessary checks for undefined when statusCode === 200.

const ar1: ApiResponse<Test> = { statusCode: 200, data: { value: 5 } };
if (ar1.statusCode === StatusCode.OK) {
  console.log(ar1.data.value);
}

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

Error: Module './App' not found in webpack module

I am encountering the error Uncaught Error: Module not found: Can't resolve ./App' and ./store in client/src. in the console of my local environment when I execute npm start from the client directory. The console is showing 2 errors: ERROR in ...

Transform the object into an array of JSON with specified keys

Here is a sample object: { labels: ["city A", "city B"], data: ["Abc", "Bcd"] }; I am looking to transform the above object into an array of JSON like this: [ { labels: "city A", data: "Abc" }, { labels: "city B", data: "Bcd" }, ]; ...

Is it possible to define a unique function signature in a child class when implementing a method from a parent class?

In the process of developing a repository module that includes generic methods, I have found that as long as each derived class has the `tableName` configured, the basic query for creating, finding, or deleting records remains consistent across all child c ...

When working with Typescript, an error is thrown if property "p" does not exist on one of the classes that are OR

In my component class, I have a property called renderContent which can be of either LessonPageType or TaskPageType based on the input value. Below is the code snippet from my component: import {ChangeDetectionStrategy, Component, HostListener, Input, OnI ...

How to extract a JavaScript object from an array using a specific field

When dealing with an array of objects, my goal is to choose the object that has the highest value in one of its fields. I understand how to select the value itself: Math.max.apply(Math, list.map(function (o) { return o.DisplayAQI; })) ... but I am unsur ...

What is the best way to centralize JSDoc typedef information for easy sharing between different projects?

Currently, I am incorporating @typedef JSDoc comments at the beginning of my Javascript files to define types (primarily to access certain benefits of TypeScript without fully diving into it right now). I'm curious, where can I keep JSDoc typedef inf ...

Choosing a single element through viewChild using the "#" selector in Angular 2

Is there a special method to choose multiple tags on the same level with the same tag? <div #el></div> <div #el></div> <div #el></div> I keep getting an error message that says "Reference "#el" is defined several times ...

What is the best way to ensure an observable has been updated before proceeding with additional code execution?

Is an observable the best choice for providing live updates of a variable's value to another class? I have a loop in my service class that looks like this: elements.forEach(element => { doStuff(); this.numberSubject.next(valueFromDoStuff); }) ...

`How can I extract HTMLElements from slots in vue3?`

When attempting to develop a Layer component, I encountered some challenges. Here is the code: // Wrapper.vue <template> <slot v-bind="attrs"></slot> </template> <script lang="ts" setup> import { defi ...

Utilizing WebWorkers with @mediapipe/tasks-vision (Pose Landmarker): A Step-by-Step Guide

I've been experimenting with using a web worker to detect poses frame by frame, and then displaying the results on the main thread. However, I'm encountering some delays and synchronization issues. My setup involves Next.js 14.0.4 with @mediapip ...

Modify the dropdown menu title dynamically based on the selection made in Angular

My Angular web-application has a dropdown menu that looks like this: <div class="btn-group" dropdown> <button dropdownToggle type="button" class="btn btn-primary dropdown-toggle">NAMEOFDROPDOWN <span class="caret"></span>&l ...

Tips for extracting key values from an array of objects in Typescript

I am working with an array called studyTypes: const studyTypes = [ { value: "ENG", label: "ENG-RU", }, { value: "RU", label: "RU-ENG", }, ]; Additionally, I have a state variable set ...

I rely on the angular-responsive-carousel library for my project, but unfortunately, I am unable to customize the arrow and dots

When it comes to CSS, I utilize ng deep style in Angular 10 to make changes for browser CSS. However, I am facing an issue where the problem is not being resolved by my CSS code. Here is a snippet of my code: > ::ngdeep .carousel-arrow { > b ...

What is the process for importing a component at a later time?

I am attempting to import components with a delay in a seamless manner. My goal is to import the components discreetly so that they load smoothly in the background while viewing the homepage. I experimented with lazy loading, but found that it caused dela ...

What are some ways to control providers in targeted tests using ng-mocks?

I recently started utilizing ng-mocks to streamline my testing process. However, I am struggling to figure out how to modify the value of mock providers in nested describes/tests after MockBuilder/MockRender have already been defined. Specifically, my que ...

Having trouble loading extensive amounts of data into a select element within an Angular application

Upon successfully retrieving around 14000 data entries from an HTTP request, I am facing difficulties loading this vast amount of data into my Select Tag. This is causing the entire page to slow down. The structure of the select Tag in question is as follo ...

Verify the occurrence of an element within an array inside of another array

Here is the scenario: const arr1 = [{id: 1},{id: 2}] const arr2 = [{id: 1},{id: 4},{id: 3}] I need to determine if elements in arr2 are present in arr1 or vice versa. This comparison needs to be done for each element in the array. The expected output sho ...

A windows application developed using either Javascript or Typescript

Can you provide some suggestions for developing Windows applications using Javascript, Typescript, and Node.js? ...

The Generic Function's Return Type in Typescript

The latest addition of ReturnType in TypeScript 2.8 is a highly valuable feature that enables you to capture the return type of a specific function. function foo(e: number): number { return e; } type fooReturn = ReturnType<typeof foo>; // numbe ...

"Exploring Angular: A guide to scrolling to the bottom of a page with

I am trying to implement a scroll function that goes all the way to the bottom of a specific section within a div. I have attempted using scrollIntoView, but it only scrolls halfway down the page instead of to the designated section. .ts file @ViewChild(" ...