typescript interface, mandatory if another is present

Is it possible to create an interface with multiple properties where one is required if the other is used?

For example:

interface MyProps {
  onPressAll: () => void;
  icon?: ImageSourcePropType;
  onPressIcon?: () => void;
}

What I'm looking for is this: if icon is set, then onPressIcon must be present (or vice versa), otherwise neither should be used.

For instance, when calling my component:

<MyComponent
   onPressAll={() => {}}
   icon={myIcon}
/>
// An error should be returned because onPressIcon is missing

<MyComponent
   onPressAll={() => {}}
   onPressIcon={() => {}}
/>
// An error should be returned because icon is missing

<MyComponent
   onPressAll={() => {}}
/>
// OK

<MyComponent
   onPressAll={() => {}}
   onPressIcon={() => {}}
   icon={myIcon}
/>
// OK

Thank you!

Answer №1

Using an interface won't work for this situation. Instead, you'll need to implement a union type:

type MyPropsCommon = {
  onPressAll: () => void;
} 
type MyProps = MyPropsCommon & (
    | { icon?: undefined, onPressIcon?: undefined}
    | {
        icon: ImageSourcePropType;
        onPressIcon: () => void;
    })

Playground Link

Answer №2

Employ the concept of Discriminating Unions.

interface PropsWithIcons {
  onPressAll: () => void;
  hasIcon: true
  icon: ImageSourcePropType;
  onPressIcon: () => void;
}

interface PropsWithoutIcons {
  hasIcon: false
  onPressAll: () => void;
}

type MyProps = PropsWithIcons | PropsWithoutIcons

var obj: MyProps = {} as MyProps

if (obj.hasIcon) {
  console.log(obj.icon, obj.onPressIcon)
}
else {
  console.log(obj.icon, obj.onPressIcon) // errors
}

sandbox

Answer №3

To expand on functionalities, consider extending interfaces:

interface SharedProperties
{
    onClickAll: () => void;
}

interface IconProperties extends SharedProperties
{
    iconImage: ImageSourcePropType;
    onClickIcon: () => void;
}

You can also merge types to ensure all properties are mandatory:

interface OtherProperties 
{
    otherValue: number;
}

type CombinedProps = IconProperties & OtherProperties;

Choose the approach that best fits your needs.

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

I am having trouble figuring out the issue with the state and how to properly implement it in Typescript

I am having difficulties sending emails using Nodemailer, TypeScript, and NextJS. The contact.tsx file within the state component is causing errors when using setform. As a beginner in TypeScript, I have been unable to find a solution to this issue. Any he ...

Getting systemjs to load a module from a CDN in global format: The ultimate guide

I keep encountering the following error: EXCEPTION: ReferenceError: cast is not defined in [null] To create a custom Chromecast receiver application, you need to utilize a specific js file that exposes the necessary functionality through a global 'ca ...

Error: The checkbox was clicked, but an undefined property (includes) cannot be read

Link to live project preview on CodeSandbox Visit the product page with checkbox I have developed a code snippet that allows users to filter products by checking a box labeled "Show Consignment Products Only", displaying only those products with the term ...

Is it necessary for the React generic type prop to be an extension of another type

I have recently started using TypeScript and I am facing a confusion regarding passing generic types into my higher-order component (HOC). My objective is to pass the component props as a generic type in order to have the Component with those specific type ...

Ways to trigger an Angular function once and persist it even after the component is reloaded

Currently, I am learning Angular and have come across a particular issue. As far as I understand, everything placed within the `ngOnInit` method gets executed every time the component reloads. I have a timer function that needs to continue running even aft ...

Error message in VsCode plugin stating that property 'X' is not found on type '{}' within a Vue 3 template

Currently, I am in the process of setting up my vue-cli project that utilizes the composition API with <script setup> to fully integrate TypeScript. Each time I try to use variables within template tags, VSCode flags errors. I have already installed ...

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 ...

Unable to place value into an array following the invocation of a function in Angular 9

Within an array I established, I am encountering an undefined value when I use console.log. Take a look at my component.ts below: export class OrderExceptionReportComponent implements OnInit { public sessionData: ExceptionReportSessionData[] = []; n ...

React: The Material-UI autocomplete input, controlled with the React Hook Form `<controller>` component, experiences issues when the `multiple` prop is set to `true`

Currently facing challenges with managing an autocomplete MUI component using the <controller> component in react-hook-form. Take a look at the code snippet below: <Controller control={control} name="rooms" render={({ field }) =&g ...

Facing a challenge with handling HTTP data in a TypeScript-based Angular web application

I am currently working on developing a web application using Angular and the SpringMVC Framework. One of the tasks I'm facing is loading a list of users (referred to as "consulenti" in the code). While the backend HTTP request works fine, I encounter ...

What is the best way to iterate through an object of objects in Vue.js using mapping?

I am looking to develop a function that maps different items to specific color thresholds. Here is an example of what I have in mind: export const mapMetricsToValue: any = { item1: { 0: 'green--text', 0.3: 'red--text&apo ...

Rearranging items within an array in a React component

Currently, I am facing a situation where I have created a list that dynamically adds a React Node upon clicking a button. The final layout of the model looks like this: https://i.sstatic.net/fG37r.png Here is the code snippet for your reference: import ...

NgrxStore - An initial item has been added twice to the array

Currently experimenting with ngrx store and manipulating elements within an array, such as deleting, fetching, and editing, works smoothly. However, a challenge arises when inserting an object into the array for the first time, duplicating the entry unless ...

Creating a custom theme in MUI v5 by modifying ColorPartial members

I am seeking a solution to override certain members within PaletteOptions. My goal is to switch the type of grey from ColorPartial to PaletteColorOptions in order to include additional members. This was my attempt at implementing the necessary code: decl ...

The element 'x' is not found within the 'unknown' type

I've been struggling with this issue. After searching through various sources like stackoverflow and github, I attempted a solution which involved adding a generic but I encountered the error message Expected 0 type arguments, but got 1. in relation t ...

Is it possible for Next.js to retrieve the window size without resorting to a faulty hook call or encountering an undefined window

In my ongoing efforts to dynamically adjust the size of an image within a next.js application to make it responsive to various screen sizes, I have encountered challenges. The different methods I have attempted and observed have resulted in either an inv ...

The preflight request in Angular2 is being rejected due to failing the access control check: The requested resource does not have the 'Access-Control-Allow-Origin' header

I encountered an issue while attempting to execute a basic POST request to establish an account using an API in .NET. The process fails with the mentioned warning title. Interestingly, performing the same request in Postman (an API testing tool) yields a s ...

Modifying the parent element's property when at least one child is selected: A simple guide

I am working with a tree structure made up of nodes called LayerNode. Each node has references to its children and parent. My goal is to update the property of a parent node if any of its child nodes have the property selected = true. public recursiveSe ...

What steps do I need to take for the function to accurately determine the return type?

class Foo { name: string; constructor({name}: {name: string}) { this.name = name; } } class Bar<T extends Foo> { foo: T; constructor({foo}: {foo: T}) { this.foo = foo; } } class CustomFoo extends Foo { xxx: string; constr ...

Tips for creating a typescript module definition that exports a module dependency as one of its members

Let's consider a particular situation: I am in the process of creating typescript definitions for two commonJS modules, A and B. Module B has a dependency on module A, and to make things easier, B directly exports A as a property B.A so that users do ...