Can we find a more effective method for describing a conditional type?

My API follows the HATEOAS architecture, providing me with structures based on my request. Whether it's a single entity or an array of entities, at the core of these responses lies valuable data.

// single entity
{
    links: [{ ... }],
    data: {
        links: [{ ... }],
        data: {
           id: 1,
           title: "example entity"
        }
    }
}

// array of entities
{
    links: [{ ... }],
    data: [{
        links: [{ ... }],
        data: {
           id: 1,
           title: "example entity"
        }
    }, {
        links: [{ ... }],
        data: {
           id: 2,
           title: "another entity"
        }
    }]
}

All responses are generically typed in the following way:

type Link = {
    href: string;
    ref: string;
    type: string;
}

type EntityResponse<T> = { links: Link[], data: T };

type Response<T, Type extends [] | undefined> = {
    links: Link[];
    data: Type extends []
        ? EntityResponse<T>[]
        : EntityResponse<T>;
}

Ideally, I would like to specify the data using the entities type:

Response<Product>

// or

Response<Product[]>

However, due to each entity being concatenated with a links object, it becomes more complex. The solution above was my workaround for this issue.

If I need to define an array type response, I would use:

Response<Product, []>

For a single entity, the typing would be as follows:

Response<Product>

I suspect that there might be a way to simplify the conditional type used, but I'm unsure how to do so without adding the links key/value pair to the entity type itself. Any guidance on this matter would be greatly appreciated!

Answer №1

By utilizing a conditional type that can deduce the potential array elements, you can effectively use Response<Product> and Response<Product[]> to specify your response types:

type Response<T> = {
    links: Link[];
    data: T extends (infer E)[]
        ? EntityResponse<E>[]
        : EntityResponse<T>;
}

Interactive code link

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

Is it possible to create my TypeORM entities in TypeScript even though my application is written in JavaScript?

While I find it easier to write typeorm entities in TypeScript format, my entire application is written in JavaScript. Even though both languages compile the same way, I'm wondering if this mixed approach could potentially lead to any issues. Thank yo ...

Using ngFor to connect input with the Algolia Places feature

I have implemented an Algolia Places input within an ngFor loop using Angular8. The issue I am facing is that the (change) event only works properly after typing in the input for the second time. While this is functional, it's not exactly the behavior ...

Filtering JSON data with Angular 4 input range feature

Hello there, I'm currently working on a search pipe in Angular 4 to filter company team sizes from JSON data using a range input between 0 and 100. However, I'm facing an issue with the range filter as I am relatively new to TypeScript and Angula ...

Having trouble locating the name 'div' when starting a new React project?

After creating a new React application using the command: "npx create-react-app poi-search --template typescript", ESLint prompted me for permission and then displayed several errors. You can view the screenshot here. I am currently using the latest versi ...

Instantiate an object of the ng.IQService type without using injection

Is it possible to define a local variable in the controller of type ng.IQService ( private _q: ng.IQService;) without requiring injection? My technology stack includes typescript and angular. The reason for this requirement is due to existing legacy code ...

Navigating through an interface array using *ngFor in TypeScript

After successfully implementing an interface to retrieve data from a service class, I encountered an issue when attempting to iterate through the FilteredSubject interface array. Despite using console.log, I was unable to achieve the desired outcome. You ...

Jest - managing parent class methods during unit tests

The code snippet provided demonstrates a class called First extending TelemetryFramework with specific props and states, containing a method named getData. This method retrieves confidential data and logs telemetry information. However, running unit tests ...

Show the same values only once when using the ngFor directive in Angular, while the remaining values continue to loop

Here is a sample data set retrieved from the server: [ { subMenuId: 1, submenu: 'Users', menu: 'Administration', url: '/pms/admin/usrs-dsh', icon: 'fas fa-cogs', }, ...

Accessing React.FC in Another File with TypeScript - A Step-by-Step Guide

code - const Exne: React.FC <IProps> = ({x}) => { console.log('input', x); const [getx, assignx] = useState(x); console.log(getx, assignx); return(getx) }; Could you please provide instructions on how to acc ...

Arranging an array of objects in a structured format

I'm working with an array of objects that looks like this const obj = [{ '1': 'a'},{ '2': 'b'}, {'3': 'c'}] and I need to extract the keys and values separately, like so: ['1',&apo ...

Issue with Jest mock function failing to trigger axios instance function causing it to return undefined

I initially found a solution on this StackOverflow thread However, I wanted to add my own helper function that generates a fresh Axios instance with the user's authentication token. Here is what I came up with: import axios from "axios"; c ...

Using Sigma.js in React with Typescript: A Comprehensive Guide

I'm attempting to set up a basic web application using React and TypeScript in order to experiment with Sigma.js graphs. However, I am facing issues as the end result does not display anything with Sigma. These are the steps I have taken: $ npx crea ...

Injecting Dependencies with Angular 2 and the Ability to Include Optional Parameters

One issue I'm facing is that I have multiple components in my Angular 2 application that require the same dependency. This specific dependency needs a string for the constructor. How can I instruct angular2 to use a specific instance of this type for ...

It is impossible to search within a read-only array union

Is there a way to search for an element within a readonly array union in TypeScript? const areas = { area1: { adjacencies: [2, 3, 4, 5] }, area2: { adjacencies: [6, 7, 8] } } as const; let area: keyof typeof areas; if (Math.random() < 0. ...

Struggling to fix TypeScript error related to Redux createSlice function

Here is the code snippet I am working on: import { Conversation } from "@/types/conversation"; import { PayloadAction, createSlice } from "@reduxjs/toolkit"; const initialState: Conversation | null = null; export const conversationSli ...

Using TypeScript with VSCode's Vetur Vue package may result in errors such as "Cannot locate symbol 'HTMLElement,' 'window,' or 'document'."

After much research, I'm still struggling with a minor Vetur issue in my Vue3 + ts setup. Despite trying various modifications to the tsconfig file recommended by others, none of them have resolved the warnings I'm encountering. I attempted to i ...

Unable to access data from the Array by passing the index as an argument to the method

Having trouble retrieving an item from an Array using method() with an index argument that returns undefined export class DataService { public list = [ { id: 11, name: 'Mr. Nice' }, { id: 12, name: 'Narco' }, ...

Create a global variable by importing an external module in TypeScript

I am currently developing a TypeScript npm module called https://www.npmjs.com/package/html2commonmark. This module is versatile and can be utilized in nodejs (via require) as well as in the browser (by loading node_modules/html2commonmark/dist/client/bund ...

Encountered an error with create-react-app and MaterialUI: Invalid hook call issue

I am encountering an issue while trying to set up Create-react-app with Material UI. The error message I receive pertains to Hooks. Could there be something else that I am missing? This is the specific error message being displayed: Error: Invalid hook ...

Utilizing CDK to transfer files to S3 storage bucket

I've been trying to upload a file to an S3 bucket created using CDK, but I keep encountering the same error even when using AWS's example code. Here is the stack: export class TestStack extends cdk.Stack { public readonly response: string; ...