Automatically divide the interface into essential components and additional features

Consider the following interfaces:

interface ButtonProps {
    text: string;
}

interface DescriptiveButtonProps extends ButtonProps {
    visible: boolean,
    description: string;
}

Now, let's say we want to render a DescriptiveButton that utilizes the properties defined in the interface.

class DescriptiveButton extends React.Component<DescriptiveButtonProps, {}> {
    render () {
        const { visible, description, ...rest } = this.props;
        return visible ? <div>{description}: <Button {...rest}/></div> : <div />;
    }
}

The issue arises when we have to manually list out all the extra props like visible and description. The desired outcome is to split DescriptiveButtonProps into ButtonProps and automatically include the extra properties without having to specify them individually. Is there a way to achieve this?

Answer №1

To divide an object into two separate objects, it is essential to specify the keys that should be included in the first output object. The following code snippet demonstrates how this can be achieved:

function split<T, K extends keyof T>(
  obj: T, keys: K[]
): [Pick<T, K>, Pick<T, Exclude<keyof T, K>>] {
  const pick = {} as Pick<T, K>;
  const unpick = {} as Pick<T, Exclude<keyof T, K>>;
  const keySet = {} as Record<K, boolean>;
  keys.forEach(k => keySet[k] = true);
  (Object.keys(obj) as (keyof T)[]).forEach(k => {
    if (k in keySet) {
      const kk = k as K;
      pick[kk] = obj[kk];
    } else {
      const kk = k as Exclude<keyof T, K>
      unpick[kk] = obj[kk];
    }
  });
  return [pick, unpick];
}

The above function can be utilized within the render() method as shown below:

const [xp , rest] = split(this.props, ["visible", "description"]);
// xp.visible, xp.description, and rest.text

While the approach above minimizes redundancy, there is still room for improvement by avoiding repetition of key names such as "visible" and "description". This can be addressed by deriving an interface from a runtime value rather than vice versa. Here's how you can achieve this:

interface ButtonProps {
  text: string;
}

const descriptiveButtonExtraProps = {
  visible: true,
  description: "string"
}
type DescriptiveButtonExtraProps = typeof descriptiveButtonExtraProps;

type PropertyIntersect<T, U> = { [K in keyof (T & U)]: (T & U)[K] }; 
interface DescriptiveButtonProps extends 
  PropertyIntersect<ButtonProps, DescriptiveButtonExtraProps> { };

By leveraging the derived DescriptiveButtonExtraProps value, we can obtain the list of keys required for splitting the object:

const descriptiveButtonExtraKeys = Object.keys(descriptiveButtonExtraProps) as 
  (keyof DescriptiveButtonExtraProps)[];

With these enhancements in place, the render() method can now be redefined as follows:

class DescriptiveButton extends React.Component<DescriptiveButtonProps, {}> {
  render () {
    const [xp, rest]: [DescriptiveButtonExtraProps, ButtonProps] = 
      split(dp, descriptiveButtonExtraKeys);
    return xp.visible ? <div>{xp.description}: <Button {...rest}/></div> : <div />;
  }
}

While the updated implementation provides a more concise way to handle repeated key names, it may introduce additional complexity. It's advisable to assess whether the benefits outweigh the added intricacy based on the specific requirements of your project.

Best of luck with your development endeavors!

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

Vue js for filtering and replacing prohibited words

For this scenario, our objective is to screen the words in our input: <input type="text" class="form-control" placeholder="Write something..." v-model="todoInput""> Below are the restricted words that we aim to substitute in the input "restrict ...

I specialize in optimizing blog content by omitting the final line within a React framework

https://i.stack.imgur.com/WKOXT.png Currently, I have 4 lines and the 5th line in the current input field. This is my React code snippet: import { FC, useEffect, useState } from "react"; interface BlogWitterProps {} const BlogWitter: FC<B ...

Tips for passing dynamic latitude and longitude values to a JavaScript function within an AngularJS framework

I am facing an issue with displaying a dynamic marker on Google Maps. I can show a static lat long marker, but I am struggling to pass dynamic lat long values to the function. How can I pass {{names.lat}}, {{names.longitude}} to the function so that I can ...

Employing ngModel within an (click) event in Angular 4

I have this html code snippet: <div class="workflow-row"> <input type="checkbox" id="new-workflow" [(ngModel)]="new_checkbox"> <label>New Workflow</label> <input type="text" *ngIf="new_checkbox" placeholder="Enter ...

When working with angular.js and angular-sanitize.js, the src attribute is removed from JSON HTML data

I'm new to Angular and I'm really enjoying learning how to work with it. Currently, I have a simple JSON file that contains text structured like this: "gettingstarted":{ "title":"Getting Started", "content":"<img ng-src='i ...

Two-way conditional type mapping

Currently, I am working on mapping the various "data types" of an object to a corresponding "schema" type. If the property's data type is boolean, it should be mapped to the "BooleanComponents" type The code snippet below demonstrates how this can ...

Wait for Axios Request Interceptor to complete before sending another ajax call

One feature I have added is a request interceptor for all axios calls. This interceptor checks the JWT token and automatically refreshes it if necessary. axios.interceptors.request.use((config) =>{ const currentState = store.getState(); // get upd ...

Creating seamless transitions between pages using hyperlinks

On the homepage, there are cards that list various policies with a "details" button above them. Clicking on this button should take me to the specific details page for that policy. However, each product can only have one type assigned to it. For instance: ...

Combining Extjs combo with autocomplete functionality for a search box, enhancing synchronization capabilities

When using autocomplete search, I've encountered an issue. If I type something and then make a mistake by deleting the last character, two requests are sent out. Sometimes, the results of the second request come back first, populating the store with t ...

Neglecting to send a socket signal while assigning a variable to a socket message

In my client-side script, I am using the following snippet: socket.on('bla', function(data) { if (data == ID) { console.log('I don't understand what's happening here.'); } }) socket.on(ID, function(data) { ...

Coding with Angular 4 in JavaScript

Currently, I am utilizing Angular 4 within Visual Studio Code and am looking to incorporate a JavaScript function into my code. Within the home.component.html file: <html> <body> <button onclick="myFunction()">Click me</button> ...

Send a function as a parameter to another component, but it remains dormant

I am attempting to control the enable and disable state of a button based on changes in a value. To achieve this, I have defined a model as follows: export class Model{ label:string=''; isEnabled:Function=()=>true; } The component1 i ...

What is the best way to transfer information from df ~ to my webpage?

I'm currently working on a pie chart that visualizes the disk space usage on my Linux machine. I need help figuring out how to properly parse this data onto a microservice URL. Any assistance would be greatly appreciated. Here's what I have so f ...

Utilizing JavaScript variables imported from an external library in Next.js: A Guide

I am currently working on a Next.js with Typescript website and I am in the process of adding advertisements. The ad provider has given me instructions to embed this JavaScript code on my site: <script src="//m.servedby-buysellads.com/monetization. ...

What could possibly be causing my app to exhaust CPU resources on Mozilla Firefox?

I have created a unique game application for Facebook. Currently, the app is not optimized with AJAX technology, resulting in multiple server requests causing high CPU usage (specifically in Firefox) which slows down the overall performance of the app. Alt ...

Having trouble getting Vue.js data to show up on the screen. I'm attempting to show a list of todos, but all that

I'm currently working on my App.vue file where I have set up the data for a todo list. However, despite creating an array of todos and attempting to display them, nothing is showing up on the screen. I'm at a standstill and could really use some ...

Preventing the upload of empty images in an angular application

When selecting multiple images for upload, I sometimes need to make changes or delete the chosen images before actually uploading them. However, if any of the selected images have a size of 0B, I want to stop the upload process for all images, not just the ...

Issue: When a function within a subscribe method does not return a value and its declared type is not 'void' or 'any', a TypeScript error occurs

In my Angular 2 project, I've created a basic service to check if the user is logged in. This service verifies the existence of the user object within the FirebaseAuth object. However, I encountered an error stating "lack of return statement" even tho ...

Creating a clickable map for a PNG image: Step-by-step tutorial

My goal is to create a interactive map similar to this one. Click here for the image ...

Is there a way for me to convert my (TypeScript Definition) .d.ts file into a (JavaScript) .js file?

It seems that there are plenty of guides available on converting a file from .js to .d.ts, but not the other way around. Specifically, I have some code in .d.ts format and I would like to convert it into JavaScript. Can anyone offer assistance with this t ...