Expanding a recursively structured interface attribute

I am working with a type called TreeItem from a library and I need to add an optional onClick function to this type (TreeItem2).

Is there a way to extend the type without duplicating the entire declaration?

This is the code I have written so far:

// Type defined in library
interface TreeItem {
    text?: string;
    items?: TreeItem[];
    // ...
};

// Extended type
type TreeItem2 = TreeItem & { items?: TreeItem2[], onClick?: () => void };

// Type usage
let item: TreeItem2 = {
    text: "a",
    onClick: () => {},
    items: [{
        text: "b",
        onClick: () => {}, // Error
    }]
};

Error message:

Type '{ text: string; onClick: () => void; }[]' is not assignable to type 'TreeItem[] & TreeItem2[]'.
  Type '{ text: string; onClick: () => void; }[]' is not assignable to type 'TreeItem[]'.
    Type '{ text: string; onClick: () => void; }' is not assignable to type 'TreeItem'.
      Object literal may only specify known properties, and 'onClick' does not exist in type 'TreeItem'.

Playground

Answer №1

To simplify achieving the desired outcome, using polymorphic this would have been the easiest method. However, this approach involves modifying the base type, which is not feasible as it resides within a library. Nonetheless, for the sake of completeness, here is how it could be implemented:

interface TreeItem {
    text?: string;
    items?: this[];
};

interface TreeItem2 extends TreeItem {
    onClick: ()=> void
}

let item: TreeItem2 = {
    text: "a",
    onClick: () => {},
    items: [{
        text: "b",
        onClick: () => {}, // ok 
    }]
};

Given that altering the library type is not an option, an alternative solution is to exclude items from the original interface and introduce a custom interface with an updated items property:

// type defined in library
interface TreeItem {
    text?: string;
    items?: TreeItem[];
};

interface TreeItem2 extends Omit<TreeItem, 'items'> {
    onClick: ()=> void
    items?: TreeItem2[]
}

let item: TreeItem2 = {
    text: "a",
    onClick: () => {},
    items: [{
        text: "b",
        onClick: () => {}, // ok 
    }]
};

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

Require a property to be mandatory depending on the value of another property within a generic interface

Looking for a way to have one property affect the others in a react component interface? Here's an example of what I'm trying to achieve: export interface IMyAwesomeComponentProps<T = {}> { className: string defaultPath?: ISomeOthe ...

What is the best approach to breaking down attributes upon import according to the theme?

Hey there! Here's the thing - I have this file called <code>colors.ts:</p> export const black = '#0C0C0C'; export const blue = '#22618E'; Whenever I need to use a color, I import it like so: import {black} from 'S ...

Is the return type determined by the parameter type?

I need to create an interface that can handle different types of parameters from a third-party library, which will determine the return type. The return types could also be complex types or basic types like void or null. Here is a simple example demonstra ...

Shifting an item between various components to modify its properties

I am currently working with an HTML table that looks like this: <table> <tr> <td>Name</td> <td>Surname</td> <td>ID Number</td> <td>Edit</td> </tr> <tr *ngFor="let p ...

storing information in localStorage using react-big-calendar

Incorporating react-big-calendar into my project, I encountered a problem where the events in the calendar would disappear upon page refresh despite saving them in localStorage. I had planned to store the events using localStorage and retrieve them later, ...

Utilizing props in styled-components: A beginner's guide

I am trying to pass a URL to a component so that I can use it as the background image of the component. Additionally, I need to check if the URL is empty. Component: <BannerImg/> CSS (styled): `const BannerImg = styled.img` background-image: url( ...

Is it feasible to select which modules to be loaded into the application?

Looking for a solution to my problem outlined in the title. For example, I am tasked with creating two separate versions of an app - one for France and one for the UK. In some areas, they require completely different implementations. Is it feasible to sw ...

Python's yield functionality facilitates the creation of lightweight generators, allowing

Request is being sent from the front end to trigger the backend method. In Python Flask, the backend method utilizes a generator and yield to iterate through a list of 100000 elements, sending each element one by one to the front end. The goal is for the b ...

Develop a new entity utilizing Array in Javascript

let DaysArray: any = ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"] I am looking to transform the above array into an Object structure as shown below: let DaysObject: any = { time: { headerName: "" }, m ...

Challenges with date formatting arise for Spanish speakers when the date returns as NaN or an Invalid

I have been working on an Angular app Objective: My aim is to allow users to input dates in Spanish format (DD/MM/YYYY) and display them as such, while converting them back to English format when saving the data to the Database. Issue: One problem I enco ...

Angular error: The function redirectToLogin in src_app_app_routing_module__WEBPACK_IMPORTED_MODULE_0__.AppRoutingModule is not defined

I'm attempting to redirect users from the homepage to the login page using home.component.ts. Within this file, I've invoked a static method called "AppRoutingModule.redirectToLogin()" that I've defined in app-routing.module.ts by importing ...

Having trouble retrieving multiple parameter values with ng bootstrap modal in Angular 4

In this section, I am creating dynamic buttons that send values to an ng bootstrap modal. Currently, I am able to send and retrieve only one value. How can I modify the code to send multiple values and display them in the input field within the modal? Belo ...

Is it possible to modify the content of an element with a click event in Angular/HTML?

How can I implement a feature in my mat-accordion using mat-expansion-panels where the text becomes underlined when the panels are clicked? I want the title to be underlined when the panels are open and for the underline to disappear when they are closed ...

Having issues with Fullcalendar's custom view called "vertical resource view" functioning improperly

I am currently using fullcalendar 4 with angular and I am trying to implement a custom view based on this example: https://fullcalendar.io/docs/v4/vertical-resource-custom-demo The view I require spans across 5 days (Monday to Friday) for just one resou ...

Empty initial value for first item in array using React hooks

My goal is to store an array that I retrieve from an API in a useState hook, but the first entry in my array always ends up empty. The initial array looks like this: (3) ["", "5ea5d29230778c1cd47e02dd", "5ea5d2f430778c1cd47e02de"] The actual data I recei ...

Having trouble with the Typescript validator in my Angular IDE Eclipse Plugin - it doesn't seem

If i enter wrong code in typescript editor it doesn't show me any compile time error. I don't know why typescript validator doesn't work. I am using eclipse Neon.3 Release (4.6.3) with angular IDE plugin. Do i have to add anything further to ...

Tips for incorporating Extract<T, U> with a nested variant?

I've encountered an issue with generated types. A particular API is providing me with two types, and I want to create distinct aliases for each. In TypeScript, we can utilize Extract<> to assist with this: type Add = { type: 'add' ...

Attempting to create a login feature using phpMyAdmin in Ionic framework

Currently, I am in the process of developing a login feature for my mobile application using Ionic. I am facing some difficulties with sending data from Ionic to PHP and I can't seem to figure out what the issue is. This is how the HTML form looks li ...

Retrieving information using React Query in TypeScript

When working with React Query and TypeScript, I encountered an issue with the getRecommendations hook. I wanted to only send the latest recommendations (I may create another hook for watchlist items in the future). The error I received was related to the q ...

Can you please provide guidance on setting the return type to React.StatelessComponent?

This code is functioning correctly: import * as React from 'react'; export default (props: any): JSX.Element => { return ( <h1>{props.children}</h1> ) } However, this snippet is causing an error: import * as React from ...