Only permitting elements of type U based on the value of T - advanced generic restrictions

Seeking assistance with adding constraints to generics in a signature. The current setup is functioning well.

Interested in implementing constraints based on the types of another type.

  async publish<T>(exchange: Exchange, routingKey: RoutingKey, message: T, options?: amqplib.Options.Publish) {
    return this.amqpConnection.publish(exchange, routingKey, message, options)
  }

Specifically looking to apply constraints on RoutingKey and "T". The constraint for "T" should be dependent on the specified RoutingKey.

Additionally, the RoutingKey should be influenced by the selected Exchange.

Here are examples of some types:

// Two types of exchanges.
export enum Exchange {
  BscDexPancakeswap = "bsc-dex-pancakeswap",
  PolygonQuickwap = "polygon-dex-quickswap",
}

export enum RoutingKey {
  PendingTx = "pending-tx", // applicable to all Exchanges
  PendingTxHash = "pending-tx-hash", // applicable to all Exchanges
  BridgeChain = "bridge-chain", // only valid when Exchange is set to PolygonQuickswap
}

// The "T" being passed into publish

export interface TxHashMsg { // valid for any Routing Key
  txHash: string
}

export interface TxMsg { // valid for any Routing Key
  tx: string
}

export interface BridgeMsg { // only valid for Routing Key = BridgeChain
  bridgeId: string
}

Essentially, incorrect routingKey should trigger a type error

this.messageBrokerService.publish<BridgeMsg>(Exchange.BscDexPancakeswap, RoutingKey.PendingTx, tx)

Similarly, an error should be thrown when the Exchange is incorrect

this.messageBrokerService.publish<BridgeMsg>(Exchange.BscDexPancakeswap, RoutingKey.BridgeChain, tx)

Valid usage: correct Exchange, correct RoutingKey, and correct Message that adhere to the defined constraints.

this.messageBrokerService.publish<BridgeMsg>(Exchange.PolygonQuickwap, RoutingKey.BridgeChain, tx)

This is a complex issue, looking for advice on feasibility and ease of implementation.

Thank you in advance

Answer №1

It appears that the approach you are taking may not seem very organic. If you have the flexibility to adjust the structure of your messages, consider the following alternative:

interface Message<T, U = RoutingKey> {
  readonly routingKey: U;
  readonly content: T;
}

export type TxHashMessage = Message<{
  readonly txHash: string;
}>;

export type TxMsg = Message<{
  readonly tx: string;
}>;

export type BridgeMsg = Message<{
  readonly bridgeId: string;
}, RoutingKey.BridgeChain>;

function publish<T, U, M extends Message<T, U>>(msg: M) {
  // ...
};

This approach ensures type safety at the message level rather than the publish function. To see a live demonstration (including a type error), you can visit this link.

It is worth noting that the base interface Message is not exported (although you can choose to modify this). However, the type aliases are exported, ideally leading users to utilize them exclusively. This decision may vary depending on your specific situation, but it is a point worth considering.

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 Encountered When Trying to Import Mocha for Typescript Unit Testing

Here's a snippet of code for testing a Typescript project using mocha chai. The test case is currently empty. import {KafkaConsumer} from '../infrastructure/delivery/kafka/kafka-consumer'; import {expect} from 'chai'; import {descr ...

Navigating through elements in Angular

I am working with multiple Angular components housed within a display:flex div container. I am fetching datatable from an API, and it contains the same number of rows as there are components. Each row in the datatable corresponds to data for each compone ...

Create a custom data type that consists of a specific set of keys from an object

Is there a way to create a type in TypeScript that includes only a subset of keys from an object? Consider the example object below: const something = { cannary: "yellow", coyote: "brown", fox: "red", roses: "white", tulipan: "purple", palmera: ...

Utilizing Angular and TypeScript: The best approach for managing this situation

I need some guidance on handling asynchronous calls in Angular. Currently, I am invoking two methods from a service in a controller to fetch an object called "categoryInfo." How can I ensure that these methods return the categoryInfo correctly and displa ...

"How to incorporate SortableJS into Ionic 3 Angular app with the help of the systemjs.config.js

I'm currently following the instructions on https://github.com/SortableJS/angular-sortablejs and I seem to be facing an issue with the systemjs.config.js file. My app is built using Ionic 3 and Angular 4. To address this, I created a script called sy ...

Tips for manually inputting dates in Primeng Calendar Datepicker with the slash "/" symbol

I need assistance with adding slashes to manual input when using the primeng Calendar component. <p-calendar [monthNavigator]="true" [yearNavigator]="true" yearRange="1950:2021" ngModel [required]="tru ...

What is the best way to implement global error handling for NextJS API requests?

Is there a method to set up a Global error handler that captures the stack trace of errors and sends it to an external system like NewRelic without needing to modify each individual API? This would follow the DRY principle by avoiding changes to multiple ...

The Angular ngx-color Color Picker is missing the option to customize the width of the color picker

I am currently utilizing the ngx-color Color Picker: https://www.npmjs.com/package/ngx-color#material This is the section where I am implementing the Color Picker and trying to specify the width <div> <div class="flex-title"> ...

The getStaticProps() method in NextJS does not get invoked when there is a change in

I have integrated my front-end web app with Contentful CMS to retrieve information about various products. As part of my directory setup, the specific configuration is located at /pages/[category]/items/[id]. Within the /pages/[category] directory, you w ...

Is it possible to meet the requirements of a specific interface using an enum field as the criteria?

I've been struggling to create a versatile function that can return a specific interface based on an enum argument, but all my attempts have failed. Could it be possible that I missed something or am simply approaching it the wrong way? If I try to ...

Tips on adding a generic type to a utility function designed for comparing object values

Looking for assistance with the syntax issue I'm encountering on this line: arr2.some((arr2Obj) => arr2Obj[identifier] === arr1Obj[identifier]) // Type 'U' cannot be used to index type 'T' I'm attempting to create a funct ...

Changing the fill color of externally imported SVGs from a CDN: A simple guide

While working on a website project using Next JS, I came across the challenge of displaying SVG icons stored in Sanity and dynamically changing their fill color. Is it possible to achieve this feature, such as changing the color when hovering over the icon ...

Unable to modify the border-radius property of Material UI DatePicker

I'm having difficulties setting rounded borders for my DatePicker component from @mui/x-date-pickers and Material UI V5. Here is the intended look I am aiming for: https://i.stack.imgur.com/c1T8b.png I've tried using styled components from Mat ...

What is the best approach for managing optional object input parameters while also verifying the presence and accuracy of that specific property?

What is the best approach to handling a situation where a method has optional object member properties for the options object, but you still want to ensure the presence of that property with a default value in the resulting instance? Is creating a separate ...

Establishing a Next.js API endpoint at the root level

I have a webpage located at URL root, which has been developed using React. Now, I am looking to create an API endpoint on the root as well. `http://localhost:3000/` > directs to the React page `http://localhost:3000/foo` > leads to the Next API end ...

Show every item from a collection on individual lines within an Angular2 module

I am working with an Angular2 component that is responsible for displaying a list of speakers stored in some data. Currently, when I add the code below to my xyz.component.html, it shows the list as comma-separated strings. However, I would like each speak ...

Struggling to deploy a Typescript React / NestJS application on Heroku due to the error message "error TS2307: Cannot find module"?

Switching from a Typescript React/Express app to using Nest.JS has caused complications when deploying to Heroku. The app runs smoothly locally, but encounters build failures on Heroku. Despite efforts to troubleshoot, it remains unclear whether the issues ...

When working with the Sequelize-Typescript One To Many Association and Repository, a situation may arise where the query returns only one child entity even though there are multiple

Dealing with Sequelize-Typescript, I recently encountered the one-to-many association involving "Album" and "Photos" entities. Each "Album" can have multiple "Photos". Below are the entity codes for reference: Album.ts ` @Table({ timestamps: true, de ...

Google Chrome does not support inlined sources when it comes to source maps

Greetings to all who venture across the vast expanse of the internet! I am currently delving into the realm of typescript-code and transcending it into javascript. With the utilization of both --inlineSourceMap and --inlineSources flags, I have observed t ...

Tips for crafting a test scenario for input alterations within Angular

Hello there, currently I am working on an application using Angular and TypeScript. Here is a snippet of my template code: <input type="text" placeholder="Search Results" (input)="searchInput($event)"> And here is the TypeScript code for the searc ...