Tips for creating a universal function for two interlinked types

My goal is to establish an abstract relationship between different sub-types of Message and Response, allowing for a generic function that takes a Message as input and returns a corresponding Response. Specifically, when the function is called with type MessageTypeA, I want the response type to always be ResponseTypeA, and I need TypeScript to enforce this connection in some way. Here's a depiction of the issue using TypeScript:

type MessageTypeA = { a: string };
type MessageTypeB = { b: number };
type MessageTypeC = [number, number];

type Message = MessageTypeA | MessageTypeB | MessageTypeC;

type ResponseTypeA = { a: boolean; aa: number };
type ResponseTypeB = "hello" | "bye";
type ResponseTypeC = number;

type Reponse = ResponseTypeA | ResponseTypeB | ResponseTypeC;

type Pair<M extends Message, R extends Reponse> = {
  message: M;
  response: R;
};

// Ideally should use a Map/Record instead
type ValidPairsMap =
  | Pair<MessageTypeA, ResponseTypeA>
  | Pair<MessageTypeB, ResponseTypeB>
  | Pair<MessageTypeC, ResponseTypeC>;

// A rough implementation attempt:
function sendMessageReturnResponse<T extends ValidPair>(
  message: typeof T.message
): typeof T.response {
  throw "uncertain how to accomplish this";
}

function main() {
  let mesA: MessageTypeA = { a: "msg" };
  let resA: ResponseTypeA = sendMessageReturnResponse(mesA);
  // error: return value of sendMessageReturnResponse cannot be inferred to be ResponseTypeA
}

In light of this challenge, what steps can I take to achieve the intended outcome?

Answer №1

If you are in need of a code snippet similar to this:

type MessageTypeA = { a: string };
type MessageTypeB = { b: number };
type MessageTypeC = [number, number];

type Message = MessageTypeA | MessageTypeB | MessageTypeC;

type ResponseTypeA = { a: boolean; aa: number };
type ResponseTypeB = "hello" | "bye";
type ResponseTypeC = number;

type Reponse = ResponseTypeA | ResponseTypeB | ResponseTypeC;

type Pair<M extends Message, R extends Reponse> = {
    message: M;
    response: R;
};

type ValidPairsMap =
    | Pair<MessageTypeA, ResponseTypeA>
    | Pair<MessageTypeB, ResponseTypeB>
    | Pair<MessageTypeC, ResponseTypeC>;

// The following function has incorrect syntax:
function sendMessageReturnResponse<Msg extends Message>(
    message: Msg
): Extract<ValidPairsMap, Pair<Msg, any>>['response'] {
    return null as any
}

function main() {
    let mesA: MessageTypeA = { a: "msg" };
    let resA: ResponseTypeA = sendMessageReturnResponse(mesA); // ok

}

Playground

In sendMessageReturnResponse, only inferring Message is necessary, allowing you to filter the union type using Extract

This line

Extract<ValidPairsMap, Pair<Msg, any>>
signifies retrieving the Pair where the message is Msg from the union of ValidPairsMap

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 issue with npm modules not appearing in EMCA2015 JS imports persists

I am currently in the process of developing a mobile application with Nativescript using the Microsoft Azure SDK. To get started, I installed the SDK via npm by running this command: $ npm install azure-mobile-apps-client --save However, upon attempting ...

Sequentially arranged are the assignments in Firebase functions

I recently came across an article on Firebase task functions published by Google, where it was mentioned that tasks should be dispatched in a first-in-first-out (FIFO) order. Despite trying different settings, the tasks are not being processed in the corre ...

Angular: Connecting template data to different visual presentations

Looking for a solution to display data and map values to another presentation without needing complex ngIf statements or creating multiple components. Check out this sample: https://stackblitz.com/edit/angular-9l1vff The 'vals' variable contain ...

When you type a letter in the middle of a string, the cursor is automatically moved back to the end - Material UI

I designed a ChipInput feature that switches to a string when focused and transforms into a Chip component when blurred, with chips separated by commas. Everything seems to be functioning properly except for one issue I am encountering. Whenever I type in ...

Issue with detecting undefined in a nested function using Typescript

Examining the code snippet provided below, focus on the test getter. Why is it that const name = this.person.name does not result in an error, while const processPerson = () => this.person.name does generate an error? interface Person { name: string; ...

Is it possible for Angular templates to be dynamic?

In my component, I have a variable named "myVar" that determines which ng-template should be displayed. Let's consider the following example of a component template: <div *ngIf="myVar; then myVar; else nothing"></div> <ng-template #foo ...

Restricting the number of mat-chips in Angular and preventing the input from being disabled

Here is my recreation of a small portion of my project on StackBlitz. I am encountering 4 issues in this snippet: I aim to restrict the user to only one mat-chip. I attempted using [disabled]="selectedOption >=1", but I do not want to disable ...

Effectively managing intricate and nested JSON objects within Angular's API service

As I work on creating an API service for a carwash, I am faced with the challenge of handling a large and complex json object (referred to as the Carwash object). Each property within this object is essentially another object that consists of a mix of simp ...

Error retrieving the latest token in Angular before the component has fully loaded

I am seeking relevant advice to address my specific need: In my Angular application, I have implemented a jwt-based authentication system. After obtaining a new token and refresh token, I have set up a setTimeout function to ensure the token is refreshed ...

Tips for eliminating unicode characters from Graphql error messages

In my resolver, I have implemented a try and catch block where the catch section is as follows: catch (err: any) { LOG.error("Failed to get location with ID: " + args.id); LOG.error(err); throw new Error(err); ...

Implementing the 'keepAlive' feature in Axios with NodeJS

I've scoured through numerous sources of documentation, Stack Overflow threads, and various blog posts but I'm still unable to make the 'keepAlive' functionality work. What could I be overlooking? Here's my server setup: import ex ...

Leveraging the Cache-Control header in react-query for optimal data caching

Is it possible for the react-query library to consider the Cache-Control header sent by the server? I am interested in dynamically setting the staleTime based on server instructions regarding cache duration. While reviewing the documentation, I didn&apos ...

Prevent the array from altering its values

I am utilizing a mock-service that is configured in the following way: import { Article } from "./article"; export const ARTICLES: Article[] = [ new Article( 1, 'used', 5060639120949, 'Monster Energy& ...

Troubleshooting the malfunction of the Angular 2 Tour of Heroes project following the separation of the app

Recently, I encountered a challenge while following a tutorial on learning Angular 2. Everything was going smoothly until I reached the point where I had to divide appcomponent into heroescomponent & appcomponent. Is there anyone else who has faced th ...

Extracting PNG file from response (bypassing standard JSON extraction)

Despite my efforts to find a solution, I am still unable to resolve this specific issue: I have implemented an Angular request (localhost:4200) to an API on Spring (localhost:8080). The HttpService successfully handles the requests, except when it comes to ...

Vitest's behavior shows variance when compared to compiled JavaScript

Challenge Currently, I am developing a package that relies on decorators to initialize class object properties. The specific decorator is expected to set the name property of the class. // index.ts const Property = (_target: any, key: any) => { } cl ...

Issue with locating module in Visual Studio 2015 when using Angular5 and TypeScript version TS2307

I'm currently attempting to integrate Angular in Visual Studio 2015 update 3, but encountering the following error: TS2307 cannot find module '../node_modules/@angular/core'. The error is shown in the image linked here. Can anyone help me fi ...

The combination of Object.keys() and the find function

Having trouble figuring out why I'm getting an error when attempting to use ES6 .find on the following data in order to retrieve the record with id number 3. { {id:10,title:'Dairy & Eggs'} {id:7,title:'Laundry & Household'} {id ...

The upcoming construction of 'pages/404' page will not permit the use of getInitialProps or getServerSideProps, however, these methods are not already implemented in my code

Despite my efforts to search for a solution, I have not found anyone facing the same issue as me. When I execute next build, an error occurs stating that I cannot use getInitalProps/getServerSideProps, even though these methods are not used in my 404.tsx f ...

Running the NPM build command results in an error specifically related to an HTML file

I encountered an issue in my AngularJS application when running the command: npm run build -- -prod The error message I received was: ERROR in ng:///home/directoryling/appname-play.component.html (173,41): The left-hand side of an arithmetic operation ...