Searching for an entity that may contain fetchable connections

Utilizing my REST API allows me the option to fetch relations based on specific requests. For instance, a basic call like GET /payment-request will return the following simplified payload:

[
  {
    "id": "01FBPR3K0RYBCRGCBWEC2AK30P",
    "payer": "01FBRKNQRP74VBWW67N4BTH40B",
    "payee": "01FB460C0FC6BSTK835VF5735J",
    "dueDate": "2021-07-31",
    "amount": 99988
  }
]

However, by adding a parameter like ?fetch=payee to the request, the response will include additional details as shown below:

[
  {
    "id": "01FBPR3K0RYBCRGCBWEC2AK30P",
    "payer": "01FBRKNQRP74VBWW67N4BTH40B",
    "payee": {
      "id": "01FB460C0FC6BSTK835VF5735J",
      "fullName": "John Doe",
      "email": "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="1973767177596d7c6a6d377a7674">[email protected]</a>"
    },
    "dueDate": "2021-07-31",
    "amount": 99988
  }
]

The ability to fetch multiple relations simultaneously is also supported, for example:

GET /payment-request?fetch=payer,payee
. While payer and payee are mandatory, there are other entities with more flexible constraints which can be nullable or point to different types.

Currently, my frontend data types are straightforward but require numerous type checks in the code:

type BusinessEntity = {
    id: string;
    fullName: string;
    email: string;
};

type PaymentRequest = {
    id: string;
    payer: string | BusinessEntity;
    payee: string | BusinessEntity;
    dueDate: string;
    amount: number;
};

In attempting to create a generic description for optional fetches, I encountered limitations when multiple fetches were requested:

type PaymentRequest<Fetched extends ('payer' | 'payee')[]> = {
    id: string;
    payer: Fetched extends ['payer'] ? BusinessEntity : string;
    payee: Fetched extends ['payee'] ? BusinessEntity : string;
    dueDate: string;
    amount: number;
};

While this approach worked for single fetches, it struggled with multiples as illustrated in the examples provided.

If anyone has suggestions on how to verify if an "array generic type" contains only one value, please share your insights!

Final Solution

Building upon captain-yossarian's solution, I have refined it further for enhanced simplicity:

type PaymentRequest<Fetched extends 'payer'|'payee'|''=''> = {
    id: string;
    payer: 'payer' extends Fetched ? BusinessEntity : string;
    payee: 'payee' extends Fetched ? BusinessEntity : string;
    dueDate: string;
    amount: number;
};

This revised syntax offers a more concise structure, allowing for seamless handling of standard outputs without needing to specify the generic every time:

let plain : PaymentRequest;                   // OK: both payer and payee are strings
let single : PaymentRequest<'payer'>;         // OK: payer is a BusinessEntity, payee is a string
let double : PaymentRequest<'payer'|'payee'>; // OK: both payer and payee are BusinessEntities

Answer №1

Do you need something similar to the following code snippet?:

type BusinessEntity = {
  id: string;
  fullName: string;
  email: string;
};

type CustomPaymentRequest<Fetched extends ('payer' | 'payee')[]> = {
  id: string;
  payer: 'payer' extends Fetched[number] ? BusinessEntity : string;
  payee: 'payee' extends Fetched[number] ? BusinessEntity : string;
  dueDate: string;
  amount: number;
};

let plain: CustomPaymentRequest<[]>;
let single: CustomPaymentRequest<['payer']>;
let double: CustomPaymentRequest<['payer', 'payee']>;

type O = CustomPaymentRequest<['payer']>['payee'] // string
type OO = CustomPaymentRequest<['payee']>['payee'] // BusinessEntity

Interactive Editor

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

What are the steps to successfully launch a Node.js / Express application with typescript on heroku?

I attempted to deploy my node.js / express server app on Heroku but encountered some issues. I followed the steps outlined in a blog post, which you can find here. Unfortunately, the deployment did not succeed. Below are snippets of the code from my serve ...

Angular 5: How to Calculate the Sum of Two Numbers and Handle NaN Output

I have encountered an issue where I am trying to multiply two numbers and add another number, but the output is displaying as NaN. How can I troubleshoot and solve this problem? Below is the code snippet: medicines = [new Medicine()]; this.sum = 0;// su ...

Unleashing the Power of Typescript and SolidJS: Expanding the Properties of JSX Elements

Is there a way to enhance the props of an existing JSX element in SolidJS and craft a custom interface similar to the ButtonProps interface shown in this React example below? import Solid from 'solid-js'; interface ButtonProps extends Solid.Butt ...

app.use not functioning properly within a class in Express

Currently, I am in the process of constructing a WebServer class with the capability to create either an HTTP or HTTPS express webserver. The primary objective of the abstract class is to establish some defaults for the express server. The child class that ...

How can I import multiple variables in TypeScript?

I have a React application built with TypeScript, and my project directory is structured as follows: App.tsx /pages Page1.tsx The contents of Page1.tsx are shown below: Page1.tsx class PageParams { constructor() { } } class Page1 { co ...

The type 'number' cannot be assigned to the type 'Element'

Currently, I am developing a custom hook called useArray in React with TypeScript. This hook handles array methods such as push, update, remove, etc. It works perfectly fine in JavaScript, but encounters errors in TypeScript. Below is the snippet of code f ...

error with angular5 material table data source

I tried multiple solutions from Stack Overflow but none of them worked for my Angular 5 project using TypeScript. I'm attempting to replicate the "Data table with sorting, pagination, and filtering" example from here. Although there are no errors, th ...

Unit testing for Angular service involving a mock Http GET request is essential for ensuring the

I am seeking guidance on how to test my service function that involves http get and post calls. I attempted to configure the spec file by creating an instance of the service, and also consulted several sources on creating a mockhttp service. However, I enc ...

Ending the connection in SignalR upon $destroy

I am currently working on a page that is utilizing SignalR, Angular, and Typescript. Upon the destruction of the current $scope (such as during a page change), I make an attempt to disconnect the client from the SignalR server: Controller.ts $scope.$on(&q ...

What is the process of destructuring an array containing objects?

Examining this JSON structure: { "Person": { "UID": 78, "Name": "Brampage", "Surname": "Foo" }, "Notes": [ { "UID": 78, "DateTime": "2017-03-15T15:43:04.4072317", "Person": { ...

Angular dynamic array binding binds to multiple elements rather than just one

In my code, I am working with an array object structured as follows: let myArray=[ { "id":"100", "child1":[ {"id":"xx","Array":[]}, {"id":"yy","Array":[]}, {"id":"zz","Array":[]} ] }, { "id":"200", "child1":[ {"id":"xx","Array ...

What function does the ng-template serve when encapsulated within an ng-select component?

Upon observing various instances of ng-select, I've noticed that it often involves wrapping a ng-template, as exemplified below: <ng-select [items]="cities" [(ngModel)]="selectedCity" bindLabel="name" bindV ...

Issues arising with code splitting using React HashRouter in a project utilizing Typescript, React 17, and Webpack 5

Encountered build issues while setting up a new project with additional dependencies. package.json: { "name": "my-files", "version": "1.0.0", "description": "App", "main": " ...

Tips for sending properties to a child component in a React Native project using TypeScript

Here is the setup in my parent component: const [OTPNotify, setOTPNotify] = useState("flex"); const closeOTPNotify = () => { setOTPNotify("none"); } <OTPRibbonComponent onCancel={closeOTPNotify} display={OTPNotify}/> Now, ...

Tips for expanding/collapsing accordion tab with the click of a button

I need help with my accordion setup. Right now, I can expand/collapse an accordion tab by clicking anywhere on the tab. However, I would like to only be able to expand/collapse the tab when clicking on the "Click me" button and not on the accordion tab its ...

What is the best way to connect Classes and Objects in Angular5?

Picture a study tool with flashcards and different categories. Each category has a title, while each card contains content, is linked to only one category, and is also connected to another card. How can I establish these connections in Angular 5 and Types ...

using any class as a function parameter in TypeScript

Looking for advice: I am new to TypeScript and have classes defined like the ones below: export declare class SampleOne extends Setting { getValue(): Promise<boolean>; setValue(value: boolean): Promise<void>; } And export declare class ...

Extract HTML content using CKEditor

Hey there! I'm in need of some help with getting user-entered data from a textarea. I've already attempted using CKEDITOR.instances.editor1.getData() and CKEDITOR.instances.ckeditor.document.getBody.getHtml(), but unfortunately both only return ...

Typescript iterative declaration merging

My current project involves creating a redux-like library using TypeScript. Here is an example of the basic action structure: interface ActionBase { type: string; payload: any; } To customize actions for different types, I extend the base interface. ...

Understanding and parsing JSON with object pointers

Is it possible to deserialize a JSON in typescript that contains references to objects already existing within it? For instance, consider a scenario where there is a grandparent "Papa" connected to two parents "Dad" and "Mom", who have two children togeth ...