Building a dynamic class in React with Typescript based on a prop value

Within my component, I am passing a prop named HtmlTag, and based on its value, I want to dynamically extend the Prop Interface with the appropriate props for the corresponding HTML tag.

I attempted to create a Type that functions as a map for this purpose, for example:

type AllAttrs = {
  a: AnchorHTMLAttributes,
  button: ButtonHTMLAttributes
}

Currently, I am trying the following approach:

interface Props extends AllAttrs["a"] {
  sanitize?: string;
}

But I encounter an error stating "an interface can only extend other interfaces."

This is how I implement my component:

<Element
   Component="a"
   ... more props
>Test</Element>

The objective is to have TypeScript notify me of any missing props (e.g., href) that are not passed when using the component.

Answer №1

To achieve an interface that extends an indexed type query, it must be placed within a separate type alias rather than directly in the extends clause.

However, a more suitable approach would be to use an intersection type in the props to ensure proper inference (refer to this page):

type AllAttrs = {
  a: AnchorHTMLAttributes<HTMLAnchorElement>,
  button: ButtonHTMLAttributes<HTMLButtonElement>
}

function Element<T extends keyof AllAttrs>({ Component, ...rest}: { Component: T} & AllAttrs[T]){
  return <Component {...rest as any} ></Component>
}

let r = <Element
  Component="a"
  href=""
>Test</Element>

//Error href not on button
let r2 = <Element 
  Component="button"
  href=""
>Test</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

Angular version 12 (node:3224) UnhandledPromiseRejectionWarning: Issue encountered with mapping:

Trying to generate the translation file in my Angular project using the command ng extract-i18n --output-path src/translate, I encountered this error message: \ Generating browser application bundles (phase: building)...(node:3224) UnhandledPromiseRej ...

Guide to making a Typescript interface by combining elements from two separate interfaces without utilizing inheritance

Programming Language: Typescript I am looking to combine the properties of two interfaces as the value of an indexable-type within a third interface. Interface 1: export interface Employee { id: string name: string } Interface 2: export interfa ...

Surprising fraction of behavior

Looking for some clarification on the types used in this code snippet: interface UserDTO { id: string; email: string; } const input: Partial<UserDTO> = {}; const userDTO: Partial<UserDTO> = { id: "", ...input }; const email = us ...

center a horizontal line using StyledSheets in your project

After drawing a horizontal line, I noticed that it is positioned towards the left side of the screen. I am hesitant to increase its width. Are there any other methods to move it to the center? I attempted wrapping it with another view and using alignConten ...

Is it possible to observe a class instance without including it in the constructor?

Currently, I'm in the process of testing my Node TypeScript application with Jest. My goal is to avoid altering my existing class, which looks something like this: export class UserRepository { async createUser(userData: User) { const pris ...

Utilizing Node.js and Jasmine: Preventing the invocation of a Promise function to avoid executing the actual code results in DEFAULT_TIMEOUT_INTERVAL

There is a function below that returns a promise. public async getAverageHeadCount(queryParams: Params, childNode: any, careerTrackId: string): Promise<Metric> { const queryId = this.hierarchyServiceApiUrl + "rolling-forecast/ahc/" + q ...

Utilize the findIndex method to search for an element starting at a designated index

Below is a snippet of code that I have: private getNextFakeLinePosition(startPosition: number): number{ return this.models.findIndex(m => m.fakeObject); } This function is designed to return the index of the first element in the array that has ...

Tips for avoiding swiper from interacting with the content upon reaching the final swiper slide button

Can anyone help me with a problem I'm experiencing? When navigating using the next or previous buttons, I always come across one button that is grayed out, indicating that I cannot move forward (for the next button) or backward (for the prev button) a ...

What is the significance of having nodejs installed in order to run typescript?

What is the reason behind needing Node.js installed before installing TypeScript if we transpile typescript into JavaScript using tsc and run our code in the browser, not locally? ...

Tips for customizing Material UI CSS default properties in React

I'm currently working on a React project and utilizing the 'Table' component from Material UI. The default CSS properties of this table, along with its components like TableHead, TableCell, and TableRow, are proving difficult to override whi ...

Is it possible to utilize useEffect for verifying the existence of the user token within the localStorage?

I am in the process of developing a web application that requires authentication. I am wondering if it is effective to create a private route by adding a condition in the useEffect hook of one of my pages. The idea is to check if a token is present before ...

Injecting Services Error in Angular

I'm in the process of developing a web App and recently put together a new service: import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class ModuleService { constructor(private startTime: ...

What is the correct method for typing sagas?

After diligently following the official redux documentation for integrating with TypeScript, which can be found at https://redux.js.org/recipes/usage-with-typescript, I successfully typed actions, reducers, react components, and more. However, my progress ...

When attempting to create a generic key value interface, Typescript will throw an error if a mapped type is used that declares properties or methods

I am attempting to design an interface that can accept generic key-value pairs in the following format export interface GetModel<K extends string, T> { [key in K]: T; } However, I encountered this error message A mapped type may not declare prop ...

Typescript is failing to perform type checking

I'm encountering an issue while trying to utilize TypeScript type checking with the following code snippet: abstract class Mammal { abstract breed(other: Mammal); } class Dog extends Mammal { breed(other: Dog) {} } class Cat extends Mammal { ...

The main module's postinstall process is initiated before the sub-module's postinstall process

Update: I am seeking guidance on how to install a module from GitHub instead of npm. That's the main query. In case you're wondering why: I'm currently working on some confidential projects and prefer not to publish the code. As a result, ...

Using Angular 5 to link date input to form field (reactive approach)

I'm encountering an issue with the input type date. I am trying to bind data from a component. Below is my field: <div class="col-md-6"> <label for="dateOfReport">Data zgłoszenia błędu:</label> <input type="date" formC ...

Mongoose: An unexpected error has occurred

Recently, I developed an express app with a nested app called users using Typescript. The structure of my app.js file is as follows: ///<reference path='d.ts/DefinitelyTyped/node/node.d.ts' /> ///<reference path='d.ts/DefinitelyTyp ...

Angular 6 - Build a dynamic single page housing various applications within

I am faced with the task of developing multiple applications using Angular 6. Each application will have its own set of components, services, and more. However, there is also a need for shared services, components, directives, and other elements that will ...

The BooleanField component in V4 no longer supports the use of Mui Icons

In my React-Admin v3 project, I had a functional component that looked like this: import ClearIcon from '@mui/icons-material/Clear' import DoneIcon from '@mui/icons-material/Done' import get from 'lodash/get' import { BooleanF ...