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

Utilizing AMD Modules and TypeScript to Load Bootstrap

I am attempting to incorporate Bootstrap into my project using RequireJS alongside typescript AMD modules. Currently, my requireJS configuration looks like this: require.config({ shim: { bootstrap: { deps: ["jquery"] } }, paths: { ...

Is there a way to implement imports based on a specific condition?

I'm just starting to learn Angular and TypeScript. My goal is to redirect multiple hostnames to a single Angular 6 project because most aspects are the same, with only language and URLs varying. For example, here's what I have implemented in ap ...

Testing the Angular router-outlet using Jasmine

When testing web-app navigation using Jasmine spec with RouterTestingModule, I am facing challenges with nested fixture.whenStable().then(() => {}). For instance: After clicking on multiple links where the router-outlet changes the displayed component ...

Exploring the creation of an Angular service that utilizes HttpClient for making GET requests, with a focus on the different

I've been diving into Angular lately and I'm facing some challenges with handling get requests. If you're interested, you can check out my code on Angular Stackblitz import { HttpClient} from '@angular/common/http'; import { Inject ...

Can someone guide me on identifying the type of object in React/Typescript?

Can anyone help me remove this unnecessary definition? const [nextLocation, setNextLocation] = useState(null) as any[]; I have been struggling with this in my React Router 6 project. I've looked through the documentation but haven't found a suit ...

Tips on deactivating a div when a checkbox is selected

I am currently working with a checkbox element in my code: <md-checkbox checked.bind="addEventCommand.allDay" change.delegate="allday()">All Day</md-checkbox> When the above checkbox is true, I want to disable the following ...

The category 'Moment' cannot be assigned to the category 'Date'. The characteristic 'toDateString' is not present in the category 'Moment'

I recently integrated moment into my Angular2 application, and encountered an issue when attempting to assign the date of this week's Saturday to a variable of type date, case "weekend": this.fromDate = moment().startOf('week ...

Guide on integrating Amazon S3 within a NodeJS application

Currently, I am attempting to utilize Amazon S3 for uploading and downloading images and videos within my locally running NodeJS application. However, the abundance of code snippets and various credential management methods available online has left me fee ...

Ways to sign up for the activeDate variable in MatCalendar so you can display the month and year labels of the current active date in the

As a newcomer to Angular, I am working on creating a datepicker with a customized header. I have successfully passed a custom header for the mat-calendar component. Reference: https://material.angular.io/components/datepicker/overview#customizing-the-calen ...

Automatic type inference for TypeScript getters

It appears that Typescript infers field types based solely on the private variable of a field, rather than taking into account the getter's return type union (1) or inferring from the getter itself (2): test('field type inference', () =& ...

Hiding a div after three clicks using HTML

What is the best way to hide a div tag containing an input tag after clicking on the input tag three times using HTML and angular? ...

Experiencing an issue with mui/material grid causing errors

An error occurred in the file Grid2.js within node_modules/@mui/material/Unstable_Grid2. The export 'createGrid' (imported as 'createGrid2') could not be found in '@mui/system/Unstable_Grid' as the module has no exports. Desp ...

Using TypeScript to define a constant array as a type

I've hit a roadblock in my typescript project while trying to define a suitable type. Here's the setup: Within my project, I have the following constant: export const PROPERTYOPTIONS = [ { value: "tag", label: "Tag" }, { ...

Exploring the potential of lazy loading with AngularJS 2 RC5 ngModule

I am currently utilizing the RC5 ngModule in my Angular project. In my app.module.ts file, the import statements and module setup are as follows: import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/plat ...

Challenges with Type Casting in TypeScript

Upon reviewing a specific piece of code, I noticed that it is not producing any compile time or run time errors even though it should: message: string // this variable is of type string -- Line 1 <br> abc: somedatatype // lets assume abc is of some ...

Error message in VueJS TypeScript: Implicit declaration of type 'props' as 'any'

Currently, I am working with vue 2.6 and typescript 3.8.3. The issue arises when I attempt to apply a validator to a prop. I am encountering error message TS7006: Parameter 'props' implicitly has an 'any' type. Below is the ...

Combining files/namespaces/modules in Typescript: How to do it?

Even though I believe the solution may be simple, understanding how to merge enums across multiple files is eluding me when reading through the documentation. // a.ts enum Color{ RED, BLUE } // b.ts enum Day{ MONDAY, TUESDAY } // c ...

Error in Directive: NgControl Provider Not Found

I encountered an issue with my Directive while attempting to inject 'NgControl' and received a 'No provider for NgControl' error. Here is the structure of my File Directory: app folder |--directives folder |--myDirec ...

Enhance JQuery functionality using Typescript

Currently, I am in the process of developing a Typescript plugin that generates a DOM for Header and attaches it to the page. This particular project utilizes JQuery for handling DOM operations. To customize the plugin further, I aim to transmit config Opt ...

The specified data type is not compatible with the current context and cannot be treated as an array

Just starting out with TypeScript and I've encountered an issue that's preventing me from successfully building my app. Everything runs smoothly on my local environment, but as soon as I try to build it, an error pops up. Here's a snippet o ...