Customized Generic Types in TypeScript based on optional property conditions

In my React/Typescript app, I currently have the following code snippet -

export type GetPricingParams = {
    search_id: number,
    promo_code?: string,
};
export type GetPricingData = {
    amount: number,
    currency: string,
    search_id: number,
    applied_promocode?: {
        type: 'AMOUNT' | 'PERCENT',
        discount: number,
    },
};
export const getPricing = api.post<GetPricingData, GetPricingParams>('/api/v1/pricing');

The api.post method serves as a wrapper for the axios post method -

post: <D = unknown, P = undefined>(uri: string): ReturnType<D, P> => (params, config) =>
{
    return axios.post(uri, params, config).then(r => Promise.resolve(r.data));
},

I am keen on making the applied_promocode a required parameter in the GetPricingData if the promo_code parameter is specified in the GetPricingParams. I attempted the following approach, however, it was unsuccessful -

export type GetPricingParams = {
    search_id: number,
    promo_code?: string,
};
export type GetPricingDataCommon = {
    amount: number,
    currency: string,
    search_id: number,
};
export type GetPricingDataWithPromocode = GetPricingDataCommon & {
    applied_promocode: {
        type: 'AMOUNT' | 'PERCENT',
        discount: number,
    },
};
export const getPricing = <P extends GetPricingParams>(params: P) =>
{
    return api.post<
        P extends {promo_code: string} ? GetPricingDataWithPromocode : GetPricingData, 
        GetPricingParams
    >('/api/v1/pricing')(params);
}

Answer №1

It seems like you are on the right track, you just needed to specify GetPricingDataCommon as the second part of your condition.

Below is the working code snippet (I have omitted the actual API call as it is not crucial for the typing issue):

export type GetPricingParams = {
  search_id: number
  promo_code?: string
}
export type GetPricingDataCommon = {
  amount: number
  currency: string
  search_id: number
}
export type GetPricingDataWithPromocode = GetPricingDataCommon & {
  applied_promocode: {
    type: "AMOUNT" | "PERCENT"
    discount: number
  }
}
export const getPricing = <P extends GetPricingParams>(params: P) => {
  return (
    data: P extends { promo_code: string }
      ? GetPricingDataWithPromocode
      : GetPricingDataCommon
  ) => {}
}

getPricing({ search_id: 2 })({ amount: 3, currency: "dollar", search_id: 7 })

// error as expected
getPricing({ search_id: 2, promo_code: "promo" })({
  amount: 3,
  currency: "dollar",
  search_id: 7
})

getPricing({ search_id: 2, promo_code: "promo" })({
  amount: 3,
  currency: "dollar",
  search_id: 7,
  applied_promocode: {
    type: "AMOUNT",
    discount: 50
  }
})

You can test the code in the playground to see if there are any issues I may have missed.

If you need further clarification, feel free to ask.

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

Navigating the maze of Material UI in React with TypeScript

I have a functioning code, but I am trying to incorporate Material UI into it. However, when I replace 'input' with 'TextField', I encounter the following error: Uncaught (in promise) Error: Request failed with status code 422 at cr ...

Error encountered when upgrading to Material-UI v5 rc.0 with typescript

After updating my material-ui to version v5-rc.0, I decided to implement styled-components. However, as I was working on my Component.styles.ts file, I encountered an error: The inferred type of 'StyledStepper' cannot be named without a referen ...

Building upon the foundation: Extending a base component in Angular

I have a Base Component that is extended by its children in Angular. However, when creating a new Component using angular-cli, it generates html and css files that I do not need for the base component. Is there a way to create a Base Component without inc ...

Splitting a string in Typescript based on regex group that identifies digits from the end

Looking to separate a string in a specific format - text = "a bunch of words 22 minutes ago some additional text". Only interested in the portion before the digits, like "a bunch of words". The string may contain 'minute', & ...

Combining the next framework with a custom server written in TypeScript using the npx command may result

Suppose I want to develop an app using the following commands: npx create-next-app --typescript example-app Create server.ts Copy and paste the code from https://www.geeksforgeeks.org/next-js-custom-server/ into server.ts Replace all instances of require ...

Guide on showcasing the parameter passed in a TypeScript file on an HTML page

Just starting out with the Ionic framework and I'm attempting to show the parameter passed from the home page on my new page. I've gone through the code below but so far, nothing is showing up. Any ideas on what might be missing? Thanks in advan ...

Destructuring an object in the find method using TypeScript

I'm running into an issue with object destructuring: Property 'name' does not exist on type 'ItemType | undefined'.ts(2339) App.tsx import "./styles.css"; type ItemType = { id: number; name: string; }; export defaul ...

Tips for creating a TypeScript function that can accept an array of concatenated modifiers with the correct data type

Can I modify data using a chain of function modifiers with correct typing in TypeScript? Is this achievable? const addA = (data: {}) => { return { ...data, a: "test" } } const addB = (data: {}) => { return { ...data, ...

Having trouble getting access to FormArray content for validation due to receiving null or undefined errors

CSS Tricks <form [formGroup]="newMovieForm"> <ng-container formArrayName="actors"> <ng-container *ngFor="let actor of (actors['controls'] || []) ; let i = index"> <div [formGroupN ...

The validator is incorrectly diagnosing the input as 'invalid' when in reality it is not

Currently, I am working on creating a text field that must not be empty and should also not start with a specific set of characters (let's say 'test'). For example, I want testxyz or an empty field to be considered invalid, while anything e ...

Using lambdas in JSX attributes is not allowed because it can negatively impact rendering performance

I encountered an error when using the following code:- <FieldArray name="amenities" render={arrayHelpers => ( <div> {values.amenitieslist && values.amenitieslist.length > 0 ? ( val ...

Having trouble deploying Firebase Cloud function following the migration to Typescript

After following the steps outlined in the firebase documentation to convert my cloud functions project to TypeScript (see https://firebase.google.com/docs/functions/typescript), I encountered an error when attempting to deploy using 'firebase deploy - ...

Tip Sheet: Combining Elements from an Array of Objects into a Single Array

When I invoke the function getAllUsers() { return this.http.get(this.url); } using: this.UserService.getAllUsers().subscribe(res => { console.log(res); }) The result is: [{id:1, name:'anna'}, {id:2, name:'john'}, {id:3, name ...

How to safely add multiple objects to an array in TypeScript & React without replacing existing objects - Creating a Favorites list

I'm in the final stages of developing a weather application using TypeScipt and React. The last feature I need to implement is the ability for users to add queried locations to a favorites list, accessed through the "favorites" page. By clicking on a ...

Error message is not shown by React Material UI OutlinedInput

Using React and material UI to show an outlined input. I can successfully display an error by setting the error prop to true, but I encountered a problem when trying to include a message using the helperText prop: <OutlinedInput margin="dense&quo ...

From transitioning from AngularJS to the latest version Angular 8

I have an Angular application where I need to update some old AngularJS code to work with Angular table.html <table ngFor="let group of vm.groups" style="float: left"> <thead> <tr> <th><b>Sl. No</b ...

What is the best way to retrieve all designs from CouchDB?

I have been working on my application using a combination of CouchDB and Angular technologies. To retrieve all documents, I have implemented the following function: getCommsHistory() { let defer = this.$q.defer(); this.localCommsHistoryDB ...

The TS2583 error in TypeScript occurs when it cannot locate the name 'Set' within the code

Just started my Typescript journey today and encountered 11 errors when running tsc app.ts. Decided to tackle them one by one, starting with the first. I tried updating tsconfig.json but it seems like the issue lies within node_modules directory. Any help ...

JavaScript alert box

I'm fairly new to the world of web development, with knowledge in CSS & HTML and currently learning TypeScript. I'm attempting to create a message icon that opens and closes a notifications bar. Here's where I'm at so far: document.getE ...

Using a specific type of keys, attempting to set two instances of those keys simultaneously will result in an error of that type

Consider this scenario: type Example = { x: string, y: number } const a: Example = { x: "x", y: 1 } const b: Example = { x: "y", y: 2 } const issue = (keys: (keyof Example)[]) => { keys.forEach(key => { a[key] ...