Typescript - Conditional Type and Optional Arguments

My component has various arguments that can be passed to it:

interface Props {
  label: string;
  children?: React.ReactNode;
  withoutActions?: boolean;
  fieldKey?: KeyProperties;
  corporate: Corporate;
}

The withoutActions and fieldKey properties are utilized by a form. If the withoutActions property is set to true, then the fieldKey should not be provided.

However, if the withoutActions property is undefined, then the fieldLabel must be of type **KeyProperties**, which consists of specific predefined values.

If withoutActions is undefined, then the other properties should not be defined at all. If withoutActions is true, then the other properties should be defined according to the specific KeyProperties type.

What would be the best way to implement this functionality?

Answer №1

Similarly to the previous response, there is room for refactoring to enhance clarity. It is important to specify the expected behavior in cases where withoutActions is set to false rather than true or undefined. For the sake of this explanation, it is assumed that both false and undefined yield the same behavior. If false is not a valid option, you can easily replace withoutActions?: false with withoutActions: undefined as suggested in the earlier answer.

type Props = {
  label: string;
  children?: React.ReactNode;
  corporate: Corporate;
} & (
  {
    withoutActions: true;
  } |
  {
    withoutActions?: false;
    fieldKey: KeyProperties;
  }
)

However, it is crucial to be mindful of a potential pitfall in this approach. Due to TypeScript's structural typing, excess property checking only occurs when directly assigning an object literal. Excess property checking is not triggered when assigning an object and inferring its type. While TypeScript and React treat direct props declarations as object literals and perform the desired excess property checking, there are scenarios where assigning objects to variables with inferred types might not raise warnings about excess properties.

Take a look at this demo based on your original example. Example #1 and #2 showcase errors due to excess property checking, while example #3 does not.

const ExampleOne = () => {
  // Type error - excess property checking
  return <Component label={''} corporate={''} withoutActions fieldKey={''} />;
}

const ExampleTwo = () => {
  const props: Props = {
    label: '',
    corporate: '',
    withoutActions: true,
  // Type error - excess property checking
    fieldKey: '',
  }

  return <Component {...props} />;
}

const ExampleThree = () => {
  const props = {
    label: '',
    corporate: '',
    withoutActions: true,
    fieldKey: '',
  }

  // No type error - no excess property checking
  return <Component {...props} />;
}

Answer №2

Your requirement is not fully clear to me, but I suggest using a type alias instead of an interface. Here is an example:

type Props = {
    label: string,
    children?: React.ReactNode,
    withoutActions: true,
    corporate: Corporate
  } | {
    label: string,
    children?: React.ReactNode,
    withoutActions: undefined,
    fieldKey: KeyProperties,
    corporate: Corporate
  }

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

Implementing a Vue.js v-bind:style attribute onto a dynamically generated element post-page initialization

Let me start by explaining my current issue and dilemma: I have been tasked with converting an existing JS project into a Vue.js framework. While I could easily solve a particular problem using jQuery, it seems to be posing quite a challenge when it comes ...

Generating images using Node.js and GraphicsMagick

I'm looking for a way to generate an image using graphicsMagick and node.js. Typically, I can achieve this with the following command: gm convert -background transparent -pointsize 30 -gravity Center label:türkçee HEEEEEY.png But I need to replic ...

Accessing JSON array values in a nested controller in AngularJS can be achieved by using

How can I access a Json array value in another controller? I am working with nested controllers where the nested controller should return the first character of the first name and last name. For example, if my first name is Anand Jee, it should return AJ. ...

Tips for automating the execution of Chrome extension code on pages with infinite scrolling

Creating my debut Chrome extension, I'm developing a tool to substitute text on web pages. The current code effectively operates on content loaded by DOMContentLoaded. However, when pages employ infinite scrolling and new content is added, the text r ...

Tips on transforming a grouped object into a table organized by column with the help of Lodash

Looking at my array data: [{ id: '1234', year: 2019 , name: 'Test 1- 2019', rate: 1}, { id: '1234', year: 2020, name: 'Test 2 - 2020', rate: 2 }, { id: '1234', year: 2020, name: 'Test 3 - 2020&apos ...

Tips for utilizing loops in Node.js to store form data as objects and arrays of objects in MongoDB

Having recently started working with MongoDB, I am incorporating Node.js, Express 4, and mongoose (mongoDB) into my project. I am facing an issue when trying to save form data to mongoDB within a loop, especially because my model contains both objects and ...

Creating an array of objects using Constructors in Typescript

Utilizing TypeScript for coding in Angular2, I am dealing with this object: export class Vehicle{ name: String; door: { position: String; id: Number; }; } To initialize the object, I have followed these steps: constructor() { ...

Implementing event dispatch on Push notifications received with Workbox

In my VueJS component, I have set up a listener for the pushMessageEvent event: <template> <div> <VueBotUI :options="options" :is-open="isOpen" :bot-typing="botTyping" :inpu ...

Join our mailing list for exclusive updates on Angular 6

ingredients : Array additionalIngredients : Array In my code, I have two different methods for subscribing: this.ingredients.valueChanges.subscribe(val=> { console.log(val); } this.additionalIngredients.valueChanges.subscribe(val2=> { console.lo ...

Conserving node.js native imports for Electron with rollup

I am working on a project using Electron, Svelte, and Typescript. Initially, I used a specific template from here, but it restricted access to node.js built-in imports like fs for security reasons in the browser/electron frontend. However, I do not requir ...

Ending the connection in SignalR upon $destroy

I am currently working on a page that is utilizing SignalR, Angular, and Typescript. Upon the destruction of the current $scope (such as during a page change), I make an attempt to disconnect the client from the SignalR server: Controller.ts $scope.$on(&q ...

JavaScript - memory heap exhausted

Recently, I encountered an issue with my API written in Node.js. The purpose of this API is to read data from a MySQL database, write it into a CSV file, and allow users to download the file once the writing process is complete. Initially, everything was f ...

Show the JSON data from the server in a table format

Is there a way to neatly display data received from an Ajax response within a table format? Below is the structure of the table: <table data-toggle="table" data-show-columns="true" data-search="true" data-select-item-name="toolbar1" id="menu_table"> ...

Express Angular Node Template Render throwing an error: module 'html' not found

I am currently in the process of creating a web application using AngularJS with ui-router for routing via $stateProvider, ensuring that only the specified states are displayed in the ui-view. In my server.js file, I have set up an initial framework such ...

Embracing JQuery for Frontend Development with Node.js

I'm currently enhancing my node.js skills by utilizing express, and I've encountered a situation that is causing me some confusion. Essentially, when a user requests a webpage and the backend sends them the HTML page, how can I incorporate a Java ...

Insert title into PDF document

I am currently working on a react application that generates PDF documents easily. The libraries I have utilized are: jspdf html2canvas Below is the code snippet that demonstrates my approach: index.js: <div className="App"> < ...

Exploring React-Query's Search Feature

Looking for guidance on optimizing my Product search implementation using react-query. The current solution is functional but could be streamlined. Any suggestions on simplifying this with react-query would be greatly appreciated. import { useEffect, use ...

The output of VueJs hooks shows a blank refs object first, followed by the referenced elements

Below is the HTML VueJS code sample that I am working with: <div v-for="site in topSites" ref="ts"><a :href="site.url"> ... </div> Update: Here is the full div code: <div v-for="site in topSites& ...

Error: The function user.comparePassword does not exist or is not defined

I am facing an error that says TypeError: user.comparePassword is not a function. I have ensured that all dependencies are installed and the APP is linked to all the libraries. I tried using Postman to retrieve data, but it doesn't seem to be workin ...

What is the most effective method for creating posts, pages, and their corresponding URLs using Next.js and MongoDB

Here's a glimpse of my MongoDB data: { "_id": { "$oid": "630f3c32c1a580642a9ff4a0" }, "title": "this is a title", "paragraph": "this is a paragraph" } Now, let's t ...