How to avoid property name conflicts when extending interfaces in Typescript

Is it possible to achieve something like this using TypeScript, such as renaming a property?

interface Person {
    name: string
    age: number
}

interface Pet {
    age: string
}

interface Zoo extends Pet, Person {}

How can we prevent this error from occurring:

Interface 'Zoo' cannot simultaneously extend types 'Person' and 'Pet' Named property 'age' of types 'Person' and 'Pet' are not identical.ts(2320)

[UPDATE]

I'm seeking a solution that yields the following desired output:

interface Zoo {
    name: string
    age: number
}

Answer №1

Oh, so your goal is not to change property names; you just want to eliminate any conflicting properties from one of the interfaces.

This concept closely resembles the notion of an object spread type operator, which TypeScript currently lacks as a part of its language at the type level. Currently, when it comes to generic spreads, the type system treats them as an intersection, which is only accurate for non-conflicting properties. However, at the value/expression level, TypeScript effectively manages spreads of concrete types. As such, you can achieve the specific Zoo you desire by convincing the type system that you possess a value of type Pet, another value of type Person, and a value formed by spreading them:

interface Person {
  name: string
  age: number
}

interface Pet {
  age: string
  trained: boolean // I added this because otherwise Zoo=Person which is weird
}

declare const person: Person; // pretend we have a person
declare const pet: Pet; // pretend we have a pet
const zoo = { ...pet, ...person }; // spread them into a new variable
type ZooType = typeof zoo; // acquire the type of that variable

interface Zoo extends ZooType { }; // convert it into an interface just because

If you're hesitant about dealing with values (especially if you don't have any readily available) and wish to carry out this process purely at the type level, you can craft a spread-like type operator yourself using mapped and conditional types. A recommended implementation was suggested by one of the language designers for Merge<L,R>, which works sufficiently well with certain considerations regarding optional/readonly/etc properties.

In your scenario, where there are no optional or readonly properties involved, here is a simpler version known as Merge<L, R>:

type Merge<L, R> = R & Pick<L, Exclude<keyof L, keyof R>>;

You'll notice that it mirrors the functionality of the aforementioned value-level spread:

interface Zoo extends Merge<Pet, Person> { };

declare const zoo: Zoo;
zoo.name; // string
zoo.age; // number
zoo.trained; // boolean

Please bear in mind that if age were optional in Person, then Merge<Pet, Person> would make age optional in Zoo. This outcome may align with your intentions, but a spread wouldn't exhibit the same behavior; {...pet, ...person} would always include an age since it is mandatory in Pet. Consequently, Zoo["age"] might be something like string | number, which the previously shared Spread<L, R> operator could manage more accurately. Furthermore, neither Merge<> nor the linked Spread<> guarantee flawless handling of readonly properties, especially given the ambiguity surrounding the appropriate treatment of such properties.

Well, I hope this information proves helpful! Best of luck!

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

Exploring nullish coalescing with undefined values

My function is set up to make API calls: interface getEventsPropsShape { eventId?: string; accountId?: string; eventsStartAfter?: string; } const getEvents = async ({ eventId, accountId, eventsStartAfter, }: getEventsPropsSha ...

What is the best way to iterate through an array of arrays using a foreach loop to calculate the total number of specific properties?

For instance, if YieldCalcValues were to look something like this: [ [ 850, 500 ], [ 3, 6 ], [ 1200, 5000 ], [ 526170, 526170 ] ] I am looking to create a foreach loop that calculates the yield per for each product. How can I accomplish this correctly? l ...

Is there a way to determine if an npm package is compatible with a specific version of Angular

As I work on my project, I realize that I have many dependencies on libraries that support Angular2 but not Angular6. It can be challenging to determine if a library supports Angular2 from just reading their GitHub pages. One idea is to check the package ...

How can we ensure that only one of two props is specified during compilation?

I've designed a customized Button component. interface Button { href?: string; action(): void; } I'm looking to ensure that when a consumer uses this Button, they can only pass either href or action as a prop, not both. I want TypeScri ...

Tips for utilizing single quotation marks while logging multiple variables in console

When I write console.log("entered values are "+A+" and "+B); the tsLint gives a warning that single quotes should be used. However, I discovered that if I use single quotes, I am unable to include multiple variables in the same console ...

How can I limit generic types to only be a specific subtype of another generic type?

Looking to create a data type that represents changes in an object. For instance: const test: SomeType = { a: 1, b: "foo" }; const changing1: Changing<SomeType> = { obj: test, was: { a: test.a }, now: { a: 3 ...

What is the proper method for typing unidentified exports that are to be used in TypeScript through named imports?

Currently, I am developing an NPM package that takes the process.env, transforms it, and then exports the transformed environment for easier usage. The module is structured like this: const transformedEnv = transform(process.env) module.exports = transf ...

Traverse the elements of a BehaviorSubject named Layer_Template

I am currently facing an issue with displaying data from my BehaviorSubject. I have come across a way to iterate through a BehaviorSubject using asyncpipe which subscribes to the Observable SERVICE todo.service.ts @Injectable() export class TodoService ...

`Creating a union of prop types in TypeScript and React`

I'm facing an issue with a component prop that I need to restrict to specific strings using a union type, as shown below: type HeadingProps = { level?: 'h1' | 'h2' | 'h3' | 'h4'| 'h5' | 'h6&a ...

What is the reason for IE displaying null when the model does not exist?

Why does IE 11 render 'null' if my model does not exist? For instance: <tr> <td [innerHTML]="model?.prop1 | my-pipe"></td> </tr> Imagine this scenario: When the page loads, a request is sent to the server and the res ...

Disabling aria-label enforcement for icon-buttons in Chakra UI Typescript

Having a Chakra UI icon button, I am facing an issue with the aria-label attribute. The IconButton is within an aria-hidden section in the tree; therefore, the label becomes redundant. If I try to remove the aria-label, TypeScript throws complaints as sho ...

Using Angular to Generate a File from Form Input and Delivering it to the User

I am attempting to develop a feature in Angular 9 that takes user input from a textarea, processes it, and then presents it back to the user as a downloadable (txt) file. The structure of the form in app.component.html is as follows: <form (ngSubmit)= ...

I am encountering an issue with Wedriver.IO where screenshots of executions on a Remote Selenium Grid Hub are not being included in my Allure Reports

wdio.conf.ci.js: The following code snippet has been added. afterTest: function(test, context, { error, result, duration, passed, retries }) { if (passed){ browser.takeScreenshot(); } }, I expect to see a screenshot attachment in the bottom right corn ...

Performing several HTTP requests in a for loop in Angular 8

Within the backend, there exists an endless list of cars. Each car is designated by a unique id and has a corresponding model name. I possess a compilation of car IDs, as illustrated below: const carIds = ['abc','xyz']; My objective ...

What is the proper way to indicate the pointer to the current element within the array?

How can I modify a code that displays a list of posts? import React from "react"; type respX = { "id": any, "userId": any, "title": any, "body": any, } interface PropsI { } interface StateI { data: respX []; } export class Compone ...

Unable to access the redux store directly outside of the component

When trying to access my store from a classic function outside the component, I encountered an error while calling getState(): Property 'getState' does not exist on type '(initialState: any) => any' Below is the declaration and im ...

Customizing page layout for pages wrapped with higher-order components in TypeScript

Looking to add a layout to my page similar to the one in this link: layouts#per-page-layouts The difference is that my page is wrapped with a HOC, so I tried applying getLayout to the higher order component itself like this: PageWithAuth.getLayout Howev ...

Attempting to incorporate an npm package (specifically Howler) into an Angular 2 application

I'm facing an issue with importing Howler into my Angular 2 app as it doesn't have a typings file. Despite my efforts in searching for a solution, I haven't been able to find anything helpful. Can someone guide me on how to import "howler" i ...

Is there a way to disable tslint warnings for npm linked packages?

Currently, I am in the process of developing a set of Angular/TypeScript components within an application that utilizes this package. To facilitate the sharing of these components, I have utilized npm link. However, upon building the project, I encountered ...

typescript - instantiate an object using values stored in an array

Assume we have a model defined as follows. export interface Basicdata { materialnumber: number; type: string; materialclass: string; } We also have an array containing values that correspond directly to the Basicdata model in order, like this: ...