The requirement of the second parameter being optional or required will depend on the value of the first

Is there a way to make the second parameter of my function optional or required based on the value of the first parameter?

Here's an example code snippet:

enum Endpoint {
    USERS = '/users/:userId',
    ORDERS = '/orders'
}

type EndpointParams = {
    [Endpoint.USERS]: 'userId';
    [Endpoint.ORDERS]: void;
}

type EndpointResponse = {
    [Endpoint.USERS]: any;
    [Endpoint.ORDERS]: any;
}

function callEndpoint(endpoint: Endpoint, params?: EndpointParams[typeof endpoint]): EndpointResponse[typeof endpoint] {
    return {};
}

callEndpoint(Endpoint.USERS); // should error
callEndpoint(Endpoint.USERS, 'param'); // should pass
callEndpoint(Endpoint.ORDERS); // should pass
callEndpoint(Endpoint.ORDERS, 'param'); // should error

I am looking for a way to enforce the requirement of the params based on the presence of a corresponding key in EndpointParams. Can this be achieved and if yes, how can it be implemented?

Answer №1

If you want to streamline the arguments by encapsulating them in a single object and defining the allowed types using a union, you can achieve it like this:

enum Route {
    PROFILE = '/profile/:userId',
    SETTINGS = '/settings'
}

type Parameters = { route: Route.PROFILE; userId: string } | { route: Route.SETTINGS }

function executeRoute(parameters: Parameters): {}

Answer №2

Here's a different approach to handle this:

enum Endpoint {
    CUSTOMERS = '/customers/:customerId',
    PRODUCTS = '/products'
}

type CustomersEndPoint = {
    endpoint: Endpoint.CUSTOMERS;
    payload: {
      customer_id: number,
      name: string
    };
}

type ProductsEndpoint = {
    endpoint: Endpoint.PRODUCTS;
}

type Endpoints = CustomersEndPoint | ProductsEndpoint;

function makeRequest<Endpoint extends Endpoints['endpoint']>(...args: Extract<Endpoints, {endpoint: Endpoint}> extends {payload: infer Payload} ? [endpoint: Endpoint, payload: Payload] : [endpoint: Endpoint]) {
    return {};
}

makeRequest(Endpoint.CUSTOMERS); // will result in an error
makeRequest(Endpoint.CUSTOMERS, {customer_id: 1, name: 'John Doe'}); // will succeed
makeRequest(Endpoint.PRODUCTS); // will succeed
makeRequest(Endpoint.PRODUCTS, 'param'); // will result in an error

The makeRequest function is a generic TypeScript function that accepts multiple arguments. Let's analyze its signature:

<Endpoint extends Endpoints['endpoint']>: This generic type parameter named Endpoint extends the endpoint property of the Endpoints type, which is a discriminated union type comprising CustomersEndPoint and ProductsEndpoint.

...args: Extract<Endpoints, {endpoint: Endpoint}> extends {payload: infer Payload} ? [endpoint: Endpoint, payload: Payload] : [endpoint: Endpoint]: This rest parameter args represents the varying number of arguments passed to the function. It utilizes TypeScript's conditional types to ascertain the type of args based on the value of Endpoint:

Extract<Endpoints, {endpoint: Endpoint}> isolates the specific subtype of Endpoints with an endpoint property matching the value of Endpoint. This aids in deducing the type of the payload for that particular endpoint.

extends {payload: infer Payload} is a conditional type that verifies if the extracted subtype contains a payload property, and if so, infers the type of the payload property as Payload.

? [endpoint: Endpoint, payload: Payload] : [endpoint: Endpoint] signifies the return type of the function. If the Payload type is inferred (i.e., if the endpoint has a payload), the return type is a tuple with the endpoint and payload elements. Conversely, if no Payload type is inferred (i.e., if the endpoint lacks a payload), the return type is a tuple with only the endpoint as its element.

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

Customize the theme type with @mui/system

Is there a way to customize my theme settings in @mui/system? While using the sx prop in Stack, the theme is defined in createTheme.d.ts, but it seems like there isn't an option to extend or override it. To work around this limitation, I have been u ...

Importing a JSON or JSONC file into a vite/typescript project can be easily done

I am looking for a way to seamlessly share my routes between my actix-web backend and Vue with Vue-Router frontend without needing separate route files. I want to define the routes on the frontend without having to make any changes on the server side. If t ...

Unable to retrieve the value from the nested formGroup

I am currently in the process of setting up nested formGroup fields using HTML code. <form [formGroup]="userProfileForm" (ngSubmit)="bookUser()" class="form"> <!-- userName --> <div class="form-group"> <label for="user ...

Is importing all models into every component considered poor practice?

At my workplace, there is a practice in the legacy code where every single model is imported into all components, regardless of whether they are needed or not. For example: import * as models from '../../view-models/models' ....... let parrot: m ...

Issue encountered while generating dynamic Routes using the map function

When attempting to dynamically use Route from an array, I encounter an error. Warning: Incorrect casing is being used. Use PascalCase for React components, or lowercase for HTML elements. The elements I am utilizing are as follows: const steps = [ { ...

Rendering a Nativescript component once the page has been fully loaded

I'm currently working on integrating the WikitudeArchitectView into my TypeScript code. I've successfully registered the element in the main.ts TypeScript file: var architectView = require("nativescript-wikitudearchitectview"); registerElement ...

Attempting to transfer a username String from the client to the server using React and Typescript

I am working on a project where I need to send the username of a logged-in user from the Client to the Server as a string. Currently, I am able to successfully send an image file, but now I also need to send a string along with it. What I aim to do is repl ...

How can I effectively test the success of a form submission in next.js using jest for unit testing?

At the moment, I am in the process of developing a unit test for a registration form within my application. The main objective of this test is to ensure that the registration process can be executed successfully without actually saving any new data into th ...

Tips for storing information without using ngModel in template-driven methodology

Currently facing a dilemma where data needs to be saved to the database from Angular UI. The display format of tabular data changes dynamically based on dropdown selections, without having predefined model properties for binding. The question arises: How ...

Issue with importing in VueJS/TypeScript when using gRPC-Web

I have developed a straightforward VueJS application and am currently grappling with incorporating an example for file upload functionality. The proto file I am utilizing is as follows: syntax = "proto3"; message File { bytes content = 1; } ...

Error encountered when using withRouter together with withStyles in Typescript on ComponentName

Building an SPA using React with Typescript and Material UI for the UI framework. Stuck on a recurring error across multiple files - TS2345 Typescript error: Argument of type 'ComponentType<Pick<ComponentProps & StylesProps & RouteCompo ...

Using React and TypeScript to create multiple click handlers for different sections within the same div element

I am a beginner in the world of React, Typescript, and coding in general, so I'm not entirely sure if what I'm attempting is even possible. Currently, I have a donut chart with clickable segments sourced from a minimal pie chart found at: https:/ ...

utilizing routerLinks to transfer data and trigger a specific function

I'm having trouble passing data through the routerLink and calling the function(). It doesn't seem to be working as expected. Here's an example of my code and you can view a working demo on StackBlitz. First Component(HTML) <span [route ...

Ways to invoke the function in a separate component

How can I use ViewChild to call a method in a different component? I have a method in the piechart component that I want to access from the app component using ViewChild. In my piechart.component.ts file: export class PiechartComponent { constructor() ...

Canceling a promise in a Vuex action

I am looking to implement the ability to cancel a running promise in my Vue component, specifically a promise returned by a Vuex action. In my scenario, the Vuex action is continuously polling an endpoint for status updates, and I need the capability to s ...

Having trouble with your React/TypeScript/Redux/Thunk action not dispatching and the state remaining unchanged?

Currently, I am facing an issue while attempting to send a GET request to an API using React-Redux & TypeScript. The goal is to dispatch an action upon clicking a button (onClick event), make the request, update the state via the reducer, and finally log t ...

Transform a nested array of objects into a distinct set of objects based on the data in JavaScript or TypeScript

I have a unique situation where I am dealing with a double nested array of objects, and I need to restructure it into a specific array format to better align with my table structure. Here are the current objects I'm working with and the desired resul ...

Is it possible to initialize multiple Observables/Promises synchronously in ngOnInit()?

I am relatively new to Angular/Typescript and facing a challenge. In my ngOnInit(), I am trying to fetch settings from my backend using a GET request. After that, I need to subscribe to an observable. The observable updates the widgets' content over t ...

Enumerated types in Typescript: access the values

Below is a flagged enum I have: enum PermissionEnum { SU = 1 << 0, // 1 Administrator = 1 << 1, // 2 User = 1 << 2 // 4 } If the value given is 6, how can I achieve: An array of strings -> ['Adm ...

Exploring the world of typed props in Vue.js 3 using TypeScript

Currently, I am attempting to add type hints to my props within a Vue 3 component using the composition API. This is my approach: <script lang="ts"> import FlashInterface from '@/interfaces/FlashInterface'; import { ref } from &a ...