typescript Parameter type dependency based on value is not functioning

interface AddDataRequest{
    data:any
}
interface AddDataResponse{
    id:string
}
interface ITest{
    addData(json:AddDataRequest):Promise<AddDataResponse>
    removeData(json:AddDataResponse):Promise<boolean>
}
function testInterface<A extends keyof ITest>(action:A,req:Parameters<ITest[A]>[0],res:Awaited<ReturnType<ITest[A]>>){
    if(action === 'addData'){
        console.log(res.id); //TS2339: Property 'id' does not exist on type 'boolean | AddDateResponse'. Property 'id' does not exist on type 'false'.
    }
    else if(action==='removeData'){
        console.log(req.id); //TS2339: Property 'id' does not exist in the 'AddDateRequest | AddDateResponse' type. The property 'id' is not found in the 'AddDateRequest' type.
    }
}
testInterface('removeData', {id:'test123'},true); //But works here

I am attempting to establish a type dependency of function parameters from the first one. It functions correctly when calling the function but encounters errors within it. These errors are elaborated upon in the comments inside the code.

   

Answer №1

Dealing with the challenge of maintaining the correlation between function parameter types while narrowing one of them is a common issue.

One workaround is to group these interconnected parameters into a single object and specify the type of the object as a discriminated union representing various possibilities (achievable through a combination of a mapped type and indexed access type).

This approach allows TypeScript to effectively narrow the entire object based on the discriminant property (in this case, `action`).

// Creating a discriminated union:
type TTestFullUnion = {
    [kind in keyof ITest]: { 
        action: kind,
        req: Parameters<ITest[kind]>[0],
        res: Awaited<ReturnType<ITest[kind]>>
    }
}[keyof ITest] 

function testInterface2<ActionFull extends TTestFullUnion>(actionFull: ActionFull) {
    const { action, req, res } = actionFull;
    if (action === 'addData') {
        console.log(res.id); // Output will be AddDataResponse
    }
    else if (action === 'removeData') {
        console.log(req.id); // Output will be AddDataResponse
    }
}
testInterface2({
    action: 'removeData',
    req: { id: 'test123' },
    res: true
}); // Will work without any issues

However, implementing this solution may require slight modifications to your API design.

Try it out in TypeScript Playground

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

Data from HTML not being transferred by Angular Forms

I am facing an issue with transferring input data from HTML's <select> element to Angular Forms. Let's take a look at my code first. File Name: home-page.component.html <form [formGroup]="rForm" (ngSubmit)="addPaste(rForm.value)"> ...

Generating a d.ts file for images in Typescript using automation techniques

Currently, I am working on a React application that utilizes TypeScript and webpack. I am aware that in TypeScript, when importing an image file, it is necessary to create a d.ts file in the current directory and include the following code: // index.d.ts ...

Having trouble persisting data with indexedDB

Hi there, I've encountered an issue with indexedDB. Whenever I attempt to store an array of links, the process fails without any visible errors or exceptions. I have two code snippets. The first one works perfectly: export const IndexedDB = { initDB ...

Having trouble executing the typescript build task: Command 'C:Program' is not valid as an internal or external command

I'm currently working on converting typescript code to JavaScript and have been following the steps outlined in the documentation. To automate the compilation of .ts files, I set up a watch task triggered by pressing Ctrl+Shift+B. However, upon runni ...

The Child/Parent arguments in Typescript methods cannot be assigned

Why is this not working in TypeScript? class Parent { id: string = '' } class Child extends Parent{ name: string = '' } const fails: (created: Parent) => void = (created: Child) => { return }; const failsToo: ({ create ...

I'm looking for a way to implement a jQuery-style initialization pattern using TypeScript - how can I

My library utilizes a jQuery-like initialization pattern, along with some specific requirements for the types it should accept and return: function JQueryInitializer ( selector /*: string | INSTANCE_OF_JQUERY*/ ) { if ( selector.__jquery ) return select ...

Guide to updating component after closing MatDialog and updating data in database using Angular 6

Currently, I am in the process of learning a MEAN stack app with Angular 6. My main focus right now is on refreshing the component after making any changes, such as adding or updating new clients/cars/drivers/bookings. The issue I'm facing is that aft ...

Are there any methods to utilize Zod for validating that a number contains a maximum of two decimal places?

How can I ensure that a numeric property in my object has only up to 2 decimal digits? For example: 1 // acceptable 1.1 // acceptable 1.11 // acceptable 1.111 // not acceptable Is there a method to achieve this? I checked Zod's documentation and sea ...

Restricting a generic parameter to a combination type in Typescript

Is there a method in Typescript to restrict a generic parameter to only accept a union type? To clarify my question, I wish that T extends UnionType would serve this purpose: function doSomethingWithUnion<T extends UnionType>(val: T) {} doSomethingW ...

Tips for syncing the state data stored in local storage across all tabs with Ngxs state management

After converting the state data to base64 format using the Ngxs state management library, I am saving it. While I can retrieve all data across different tabs, any changes made in one tab do not automatically sync with other tabs. A tab refresh is required ...

Invoke a general function with corresponding generic parameters

I am currently working on a function that takes another function and its arguments as parameters, then runs the function with the provided arguments and returns the result while maintaining the data types. If the function being provided has a fixed return ...

I am facing difficulty in retrieving data from Firestore using Angular

I've been utilizing the AngularFireList provided by @angular/fire/database to retrieve data from firestore. However, despite having data in the firestore, I am unable to fetch any information from it. import { Injectable } from '@angular/core&apo ...

What are the specific purposes of utilizing semantic versioning (semver) notation within the package.json file?

Could someone clarify the specific distinctions between the semver notations found in package.json file? I'd appreciate a detailed explanation. ...

Storing the typeof result in a variable no longer aids TypeScript in type inference

Looking at the code snippet below: export const func = (foo?: number) => { const isNumber = typeof foo === 'number'; return isNumber ? Math.max(foo, 0) : 0; }; A problem arises when TypeScript complains that you cannot apply undefined to ...

What is a Mongoose Schema type in TypeScript and how can it be used as a custom

https://i.stack.imgur.com/mtlRi.png Could anyone assist me with storing a custom object that includes attributes from the StationRating interface? ...

What is the method for transmitting a concealed attribute "dragable" to my component?

Currently, I have successfully integrated a here map into my project, but I am now tackling the challenge of adding draggable markers to this map. To achieve this, I am utilizing a custom package/module developed by my company. This package is designed to ...

Require users to sign in immediately upon landing on the homepage in Next JS version 13 or 14

I have created a traditional dashboard application using Next.js 13 with a pages router that places all pages behind the /dashboard route, such as /dashboard/users, /dashboard/orders, and so on. I want to ensure that when a user visits the website, a fork ...

When utilizing *NgIf, the button will be shown without the accompanying text being displayed

When trying to display either a confirm or cancel button based on a boolean set in my component.ts, I implemented the following code in my HTML: <mat-dialog-actions class="dialog-actions"> <button class="cancel-btn" ...

The issue arises when attempting to use the search feature in Ionic because friend.toLowerCase is not a valid function

I keep encountering an error message that says "friend.toLowerCase" is not a function when I use Ionic's search function. The unique aspect of my program is that instead of just a list of JSON items, I have a list with 5 properties per item, such as f ...

Creating adaptable rows and columns with Angular Material's data table feature

My approach to rendering dynamic rows and columns using a basic table was successful: <tbody> <tr *ngFor="let row of data"> <td *ngFor="let val of row"> {{ val }} </td> </tr> </tbody> </ ...