Creating object-oriented designs in TypeScript: leveraging subclassing to account for differences in constructors

Working with ES6 classes and TypeScript to create a user interface, I have a base class named Control. I am looking for a way to create a Button, which is essentially an instance of Control with predefined properties. To achieve this, I have used Button as a sub-class of Control.

As I delved deeper into this implementation, I realized that my sub-class simply calls super() with the control type set to 'button'. Additionally, Button enforces certain control properties to be mandatory, leading me to question the correctness of this approach. Is using a sub-class the ideal OOP solution for this scenario? What would be a more effective way to tackle this problem?

interface ControlOptions {
  type: string;
  label?: string;
  onClick?: () => void;   // optional here
  submit?: boolean;
  transition?: Transition
}

class Control {
  constructor(options: ControlOptions) {

  }

  // Some methods left out for brevity.
}

interface ButtonOptions {
  label: string
  onClick: () => any    // mandatory here
  submit?: boolean
  transition?: Transition
}

// Is subclassing the right choice here?
class Button extends Control {
  constructor(options: ButtonOptions) {
    let controlProps = { type: 'button', ...options };

    super(controlProps);

    if (options.transition) {
      options.transition.setSource(this);
    }
  }
}

Answer №1

It appears fine to me. Just because the subclass has minimal changes doesn't render it unnecessary or incorrect. I frequently develop classes that are small or have limited functionality.

In this scenario, it is acceptable for a subclass to enforce the mandatory passing of certain data, especially when the base class allows for it. The base class anticipates a value or undefined, making it acceptable to provide a value from the subclass. Conversely, if the base class expects a value and the subclass makes it optional, it would result in a compile error.

For clarity purposes, I suggest the following modifications:

interface ControlOptions {
  label?: string;
  onClick?: () => void;
  submit?: boolean;
  transition?: Transition;
}

class Control {
  constructor(type: string, options: ControlOptions = {}) {
  }
}

interface ButtonOptions extends ControlOptions {
  onClick: () => void; // enforce as mandatory
}

class Button extends Control {
  constructor(options: ButtonOptions) {
    super('button', options);

    if (options.transition) {
      options.transition.setSource(this);
    }
  }
}

Alternatively:

class Control {
  constructor(options: { type: string; } & ControlOptions) {
  }
}

class Button extends Control {
  constructor(options: ButtonOptions) {
    super({ type: 'button', ...options });
    // etc...
  }
}

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

The design of Next.js takes the spotlight away from the actual content on the

Recently, I've been working on implementing the Bottom Navigation feature from material-ui into my Next.js application. Unfortunately, I encountered an issue where the navigation bar was overshadowing the content at the bottom of the page. Despite my ...

The Angular Material date picker unpredictably updates when a date is manually changed and the tab key is pressed

My component involves the use of the Angular material date picker. However, I have encountered a strange issue with it. When I select a date using the calendar control, everything works fine. But if I manually change the date and then press the tab button, ...

Using `useState` within a `while` loop can result in

I'm working on creating a Blackjack game using React. In the game, a bot starts with 2 cards. When the user stands and the bot's card value is less than 17, it should draw an additional card. However, this leads to an infinite loop in my code: ...

Wrapper around union function in TypeScript with generics

I'm struggling to find a solution for typing a wrapper function. My goal is to enhance a form control's onChange callback by adding a console.log. Can someone please point out what I might be overlooking? interface TextInput { type: 'Tex ...

Manage both the return of an observable and the setting of a value within a single method using

I am in need of a service that can both return an observable and set a value to a field within the same method. The current implementation of my userService.getUserDetails() method is as follows: private requestUrl: string; private bic: string; private i ...

Running RXJS Functions in a Sequence Maintained by an Array

I am trying to run a series of functions in sequence by storing them in an array (specifically for an Angular APP_INITIALIZER function). Here is the array in question: const obsArray = [ myService1.init(), myService2.init(), ... myServiceN ...

What are some effective strategies for incorporating React states with input variables?

As someone who is new to working with React, I am currently facing a challenge with my input form in React Typescript. My goal is to utilize the useState hook to store the values of various input fields such as name, email, and others. Currently, I have de ...

"Although TypeOrm successfully generates the database, there seems to be a connectivity issue

Attempting to set up a JWT authentication system using NestJs and SQLite. The code successfully generates the SQLite file, but then throws an error stating "Unable to connect to the database." Upon checking with the SQLite terminal, it became apparent that ...

The error message "Property 'map' is not found on type 'User' in React with typescript"

I am currently experimenting with React using TypeScript. I have set up useState with an object but I am encountering issues trying to use the map function with that object. Below is the error message I am facing: Property 'map' does not exist ...

What is the abbreviation for a 'nested' type within a class in TypeScript?

Consider the TypeScript module below: namespace AnotherVeryLongNamespace { export type SomeTypeUsedLater = (a: string, b: number) => Promise<Array<boolean>>; export type SomeOtherTypeUsedLater = { c: SomeTypeUsedLater, d: number }; } cl ...

Typescript: Issue encountered with Record type causing Type Error

When utilizing handler functions within an object, the Record type is used in the following code snippet: interface User { id: string; avatar: string; email: string; name: string; role?: string; [key: string]: any; } interface Stat ...

Encountering build errors while utilizing strict mode in tsconfig for Spring-Flo, JointJS, and CodeMirror

While running ng serve with strict mode enabled in the tsconfig.json, Spring-Flow dependencies are causing errors related to code-mirror and Model. https://i.sstatic.net/KUBWE.png Any suggestions on how to resolve this issue? ...

What is the best way to fetch the data from this API?

function fetchCoinPrice(coinName) { return axios .get( `https://min-api.cryptocompare.com/data/pricemulti?fsyms=${coinName}&tsyms=EUR` ).then((response) => (response.data[coinName]["EUR"])); The JSON response for the coin "BTC" is: ...

Parsing JSON objects with identifiers into TypeScript is a common task in web development

I possess a vast JSON object structured like so: { "item1": { "key1": "val1", "key2": "val2", "key3": [ "val4", "val5", ] }, { "item2": { "key1": "val1", "ke ...

Develop a cutting-edge TypeScript library that allows for seamless resolution of optional dependencies by the application

One of my recent projects involved creating a library that I published to a private npm repository. This library consisted of various utilities and had dependencies on other libraries, such as @aws-sdk/client-lambda. However, not all applications utilizin ...

Converting "require" to ES6 "import/export" syntax for Node modules

Looking to utilize the pokedex-promise for a pokemonapi, however, the documentation only provides examples on how to require it in vanilla JavaScript: npm install pokedex-promise-v2 --save var Pokedex = require('pokedex-promise-v2'); var P = new ...

What advantages does CfnAppSync provide over using AppSync in a CDK project?

We are in the process of enhancing our API by adding new JS resolvers and phasing out the VTL resolvers for an AWS AppSync CDK project, specifically built with Cfn<> Cloud Front CDK. The code snippet below illustrates how this can be achieved: ...

Issue with retrieving properties in Angular template due to undefined values in HTML

As a newbie to Angular, I am dedicated to improving my skills in the framework. In my project, I am utilizing three essential files: a service (Studentservice.ts) that emits an observable through the ShowBeerDetails method, and then I subscribe to this ob ...

I am unable to retrieve the values from a manually created JavaScript list using querySelectorAll()

const myList = document.createElement("div"); myList.setAttribute('id', 'name'); const list1 = document.createElement("ul"); const item1 = document.createElement("li"); let value1 = document.createTe ...

Transforming a string such as "202309101010" into a date entity

Need to convert a string in the format "YYYYMMDDHHMM" (e.g. "202309101010") into a Date object in TypeScript? Check out this code snippet for converting the string: const dateString: string = "202309101010"; const year: number = parseInt(dateString.subst ...