Types that depend on function input parameters

I am facing a challenge with a function:

function navigateOptions(currentScreenRoute: 'addGroup' | 'addGroupOnboarding', group: GroupEngagement) {
  const navigationKey = currentScreenRoute === 'addGroup' ? 'addGroupPeople' : 'addGroupQR';
  const params =
    currentScreenRoute === 'addGroup'
      ? {
          group,
          inviteCode: group.inviteCode,
          showGroupCreatedToast: true,
        }
      : {
          inviteCode: group.invite_code!,
        };

  return { navigationKey, params };
}

I am looking to define the return type as either:

{ navigationKey: 'addGroupPeople', params: { group: GroupEngagement, inviteCode: string, showGroupCreatedToast: boolean }}

OR

{ navigationKey: 'addGroupQR', { inviteCode: string }}

The challenge lies in the fact that navigationKey is being inferred as a string and params is becoming a union of multiple types. I want the types to be determined based on the currentScreenRoute and display the appropriate one as the type.

I suspect that Conditional Types might be the solution, but I am struggling to implement them correctly.

Answer №1

The current implementation of the code lacks a direct connection between the navigationKey and params variables, despite both being determined by a ternary operation based on the currentScreenRoute.

To achieve the desired type inference, it is suggested to build complete objects within the conditionals themselves rather than separately.

type EngagementGroup = {
  inviteCode: string;
  // ...
};

function navigateOptions(
  currentScreenRoute: 'addGroup' | 'addGroupOnboarding',
  group: EngagementGroup
) {
  const navigationKey: 'addGroupPeople' | 'addGroupQR' = currentScreenRoute === 'addGroup' ? 'addGroupPeople' : 'addGroupQR';

  if (navigationKey === 'addGroupPeople') {
    return {
      navigationKey,
      params: {
        group,
        inviteCode: group.inviteCode,
        showGroupCreatedToast: true,
      }
    }
  }
  else {
    return {
      navigationKey,
      params: {
        inviteCode: group.inviteCode,
      }
    }
  }
}

/*
Return type:
{
    navigationKey: "addGroupPeople";
    params: {
        group: EngagementGroup;
        inviteCode: string;
        showGroupCreatedToast: boolean;
    };
} | {
    navigationKey: "addGroupQR";
    params: {
        inviteCode: string;
        group?: undefined;
        showGroupCreatedToast?: undefined;
    };
}
*/

Since no types are explicitly declared, TypeScript deduces the type of params, assuming both objects share the same type but with optional properties.


A more robust approach would be to define specific types for these scenarios and use a discriminated union to represent the possible return options.

type AddGroupPeopleNavigationOption = {
  navigationKey: 'addGroupPeople',
  params: {
    group: EngagementGroup;
    inviteCode: EngagementGroup['inviteCode'];
    showGroupCreatedToast: true
  }
}
type AddGroupQrNavigationOption = {
  navigationKey: 'addGroupQR';
  params: {
    inviteCode: EngagementGroup['inviteCode'];
  }
}
type NavigationOption = AddGroupPeopleNavigationOption | AddGroupQrNavigationOption

function navigateOptions(
  currentScreenRoute: 'addGroup' | 'addGroupOnboarding',
  group: EngagementGroup
): NavigationOption {
  const navigationKey: NavigationOption['navigationKey'] = currentScreenRoute === 'addGroup' ? 'addGroupPeople' : 'addGroupQR';

  if (navigationKey === 'addGroupPeople') {
    // TypeScript expects the type of params here to match AddGroupQrNavigationOption['params']
    return {
      navigationKey,
      params: {
        group,
        inviteCode: group.inviteCode,
        showGroupCreatedToast: true,
      }
    }
  }
  else {
    // TypeScript expects the type of params here to match AddGroupPeopleNavigationOption['params']
    return {
      navigationKey,
      params: {
        inviteCode: group.inviteCode,
      }
    }
  }
}

If an incorrect params object is provided that does not align with the inferred type based on the value of navigationKey, TypeScript will now raise an error.

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

Switch from Index.html to Index.html#section1

Currently, I am working on a website and sending someone a draft for review. However, the Home screen is designed as a 'a href' link with "#home". The issue arises when the website opens from my computer; it goes to ...../Index.html instead of .. ...

Filtering content with a sliding effect using CSS and JavaScript

I'm currently developing a jQuery mobile app that requires filtering posts on a specific page. I have already added the posts and designed the filter. Take a look at how it appears below: https://i.sstatic.net/mVh9X.png I am working on animating th ...

Setting a cookie using express.js with a 'j' prefix

Trying to establish a cookie using res.cookie as shown below: res.cookie('userId',req.user._id); //cookie set here console.log(req.user._id); //correct value returned, eg abc However, I'm noticing j:"abc" in my cookie. What could be the re ...

Explore a JSON array within another JSON array

Here is a JSON format that I have: { "Project [id=1, dateDebut=2017-01-13, dateFin=2017-01-18, description=qsd, sponsor=qsd ]" : [ {"id":1,"title":"qsd ","description":"qsdqsd","dateFin":"2017-01-26"}, {"id":2,"title":"sss ","descriptio ...

The useState variable's set method fails to properly update the state variable

I am currently developing an application with a chat feature. Whenever a new chat comes in from a user, the store updates an array containing all the chats. This array is then passed down to a child component as a prop. The child component runs a useEffect ...

Group multiple typescript files into separate outFile modules

Can TypeScript files be grouped into multiple outFiles? I want to bundle my Typescript code, but instead of one single JS file, I would like to organize my TS into separate JS files such as controllers.js and plugins.js. The options in the TypeScript pro ...

Using the jquery slider in conjunction with the onchange event

I have integrated a jquery slider add-on into my project that updates a value in a Linux file whenever it is manipulated. The slider is connected to a text input box, which is set as readonly and therefore always blurred. The issue I am facing is that the ...

Maintain the previous droppable positioning after refreshing the page

I am encountering an issue with the .droppable event. I have set up two sections where I can move elements, but every time the page is refreshed, the positioning of the elements reverts to the initial position. How can I maintain the last positioning of th ...

The Google Maps geocoding service fails to provide accurate location information

I am currently attempting to utilize the Google Maps Geocoding API within my JavaScript code. Below is the snippet I have written: var geocoder = new google.maps.Geocoder(); function geocodeAddress() { var address = document.getElementById("address").v ...

Enable the button if at least one checkbox has been selected

I've written some code similar to this: $('input[type=checkbox]').click(function(event) { $('.chuis').each(function() { if(this.checked) { $('#delete_all_vm').prop("disabled",false); } ...

JavaScript hovering drop-down feature

Hi there, I'm just starting out with javascript and could use some help with a simple script. I have a shopping cart drop down that currently activates when clicked. However, I want it to fade in when hovered over instead. I've tried using .hove ...

activated by selecting a radio button, with a bootstrap dropdown menu

I'm having trouble triggering the dropdown in Bootstrap by clicking on a radio button. It seems like a simple task, but I've been struggling with it all day. According to Bootstrap documentation, you can activate the dropdown using a hyperlink o ...

Ways to determine the presence of a value in an array

Here is an example array: [ {practitioner: "place_1509136116761", H0709: false, H0911: false, H1113: false, H1315: false}, {practitioner: "place_1509136116772", H0709: true, H0911: false, H1113: true, H1315: false}, {practitioner: "place_15091361166 ...

What's the process for setting a value in selectize.js using Angular programmatically?

Currently, I am implementing the AngularJS directive to utilize selectize.js from this source: https://github.com/kbanman/selectize-ng In my scenario, I have two dropdowns and my goal is to dynamically populate one of them called selectizeVat based on the ...

"Limitation observed - function operates only on first attempt

I have been working on a map project that involves pulling information from Google about various locations using the library available at https://github.com/peledies/google-places In order to troubleshoot and identify the issue with the code related to ma ...

issue with integrating promise in angular 4

I'm currently learning about promises and how to implement them in Angular. I have written the following code in StackBlitz. My goal is to display the array whenever it contains a value by using promises in Angular. This is my HTML code: <h2>A ...

"Receiving the error message 'Object is not a function' occurs when attempting to pass a Node.js HTTP server object to Socket.IO

A few months back, everything was working fine when I set up an HTTPS server. Recently, I revisited the application and decided to switch to HTTP (though this change may not be directly related). In my code snippet below from 'init.js', I create ...

Learn how to transfer information via WebSocket when the connection closes in a React/NextJS application during a page reload or tab

In the development of my web application, I am implementing a feature to display the online/offline status of users. In order to achieve this functionality, I have to listen for both close and open events of the websocket. const ws = new WebSocket('ws ...

ReactJS Issue: Failure of Validation on Auto-Populated Form Field

I encountered an issue with the validation setup in my form. The validation checks the input values for "required", "max length", and "min length". Oddly, the validation only works for fields where the user manually types into the input field. I made some ...

How to easily deactivate an input field within a MatFormField in Angular

I've come across similar questions on this topic, but none of the solutions seem to work for me as they rely on either AngularJS or JQuery. Within Angular 5, my goal is to disable the <input> within a <mat-form-field>: test.component.h ...