"Embracing the power of multiple inheritance with Types

I am struggling with the concept of multiple inheritance in TypeScript. It doesn't make sense to overload a hierarchy with too much functionality. I have a base class and several branches in the hierarchy. However, I need to utilize mixins to isolate some core logic in separate classes because it is not required in every branch.

Here is an updated example of the code:

function Mixin = function(mixins:any[]){ // mixin decorator
    return function(target){
        mixins.forEach((mixin) => {
            add every function from mixing to target prototype, 
            if functions with same name does not exists there,
            so we are able to do calls to as example different render() functions   

            Will show it in OpenableItem        
        });
    }
}

function element = function(){
    return function(target){
        target.prototype.element = function(){
            return this.$el;
        }
    }
}
--------------------------------------------------------------------------------------
@element // every item will have this function as Root as Openable
class BaseItem{
    y() => number; // we need to get y position of every item
}
OpenableMixin{
    render() => render arrow only
    open(){} => change arrow position and cause menu to fire change event
    close(){} => change arrow position and cause menu to fire change event
}
class ItemsMixin extends OpenableMixing{// for now if item have childs it must be openable, 
                    // but Responsive has only tasks and must be openable too
    addItem(item: BaseItem) // need to add generics
    removeItem(item: BaseItem)  
}
--------------------------------------------------------------------------------------
@Mixin([ItemsMixin, ActivitiesMixin]) // it can have items and activities
class OpenableItem extends BaseItem implement ItemsMixin, ActivitiesMixin { // as in typescript docs
    render(){
        // call rendering from BaseItem class
        super.render();
        // call rendering from OpenableMixing
        OpenableMixin.prototype.render.call(this);
        // do some separate rendering for OpenableItem only
        this.$el.append('line');
    }   
}

@Mixin([ItemsMixin]) // it can have items
class RootItem extends BaseItem implement ItemsMixin{ // and subitems functionality is only for Root class
    subitems: Array<BaseItem> // need to add generics to be able to put here different item types
}
--------------------------------------------------------------------------------------
@element
class Menu{
    items: Array<Item>
}

@element
class Timeline{
    menu: Menu
    listAllelement() => {
        console.log(this.element());
        console.log(this.menu.element());
        this.menu.items.forEach((item) => {
            console.log(item.element());
            if(item.hasChilds()){  // really it must be (item instanceof RootItem || item instanceof OpenableItem)
                item.items.forEach((subitem) => { // really we need some recursion here
                    console.log(subitem.element());
                })
            }
        })
    }
}

In reality, the need for implementing multiple inheritance is rare, especially in JavaScript. However, each item may require different functionalities based on specific needs.

Consider a scenario where various items may have multiple mixins. Is it practical to incorporate everything into the base class? What approach would you take to tackle this issue?

Answer №1

TypeScript version 2.2 introduced support for the ECMAscript 2017 mixin pattern

Below is a comprehensive code example:

// References:
// http://justinfagnani.com/2015/12/21/real-mixins-with-javascript-classes/
// https://github.com/Microsoft/TypeScript/wiki/What's-new-in-TypeScript#support-for-mix-in-classes

type Constructor<T> = new (...args: any[]) => T;

class S {
  foo() {
    console.log('foo from S');
  }
}

function Mixin1<T extends Constructor<{}>>(SuperClass: T) {
  return class extends SuperClass {
    foo() {
      console.log('foo from Mixin1');
      if (super.foo) super.foo();
    }
  };
}

function Mixin2<T extends Constructor<S>>(SuperClass: T) {
  return class extends SuperClass {
    foo() {
      console.log('foo from Mixin2');
      super.foo();
    }
  };
}

class C extends Mixin1(Mixin2(S)) {
  foo() {
    console.log('foo from C');
    super.foo();
  }
}

new C().foo();

The above code snippet will produce the following output:

foo from C
foo from Mixin1
foo from Mixin2
foo from S

Answer №2

At the moment, TypeScript lacks any specific syntax for expressing multiple inheritance or mixins. However, a workaround recommended by official sources can be accessed here.

The suggested approach involves having your main class implement the desired classes, providing basic dummy implementations for their interfaces, and utilizing a special function to merge properties into your main class.

There have been proposals to enhance TypeScript's support for mixins/traits, such as those outlined in issues #2919 and #311.

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

Why is it that a static variable cannot be accessed using the `this` keyword in a static method when the static method is called in any route's controller in NODEJS?

Is it possible to access static variables in a static method using the 'this' keyword? The answer is yes, but there seems to be an issue when passing that static method in any route. The 'this' keyword refers to the class, yet its value ...

The Unit Test for Angular NgRx is not passing as expected

I'm facing difficulties with my unit tests failing. How can I verify that my asynchronous condition is met after a store dispatch? There are 3 specific checks I want to perform: 1/ Ensure that my component is truthy after the dispatch (when the cond ...

Encountering the "encoding" Module Error when Implementing Nextjs-13 with Supabase

I encountered an issue while trying to utilize Supabase for handling data insertion/retrieval from my form. Upon compilation, I received an error stating that the encoding module was not found. Despite attempting cache cleaning and re-installation of npm m ...

Unexpected JSON end causes issue with DELETE request in Next.js version 13

I am currently working on a web app using Next 13 and I have a route.ts file located in the api folder. This file contains two different methods, POST and DELETE. While both methods successfully receive the request, I am facing an issue with JSON parsing ...

I am seeking guidance for developing my own messaging platform, similar to Discord, using Typescript

Allow me to simplify this for you. This piece of code outlines TypeScript interfaces and namespaces for a WebSocket API that is commonly used in a chat or messaging system. It seems to define the format of messages being exchanged between a client and ser ...

Preventing memory leaks in unmounted components: A guide

Currently, I am facing an issue while fetching and inserting data using axios in my useState hook. The fetched data needs to be stored as an array, but unfortunately, I encountered a memory leak error. I have tried various solutions including using clean u ...

Angular File Upload Button Tutorial

English is not my first language, so please excuse any mistakes. I recently started learning Angular and I'm attempting to build a file upload button that lets users upload files based on dropdown menu options (such as USA States). Once uploaded, the ...

Is Axios the sole option for API calls when utilizing Next.js with SSG and SSR?

Can someone clarify the best practice for data fetching in Next.js? Should we avoid using axios or other methods in our functional components, and instead rely on SSG/SSR functions? I'm new to Next.js and seeking guidance. ...

Struggling to overcome the TS2322 error when assigning generic values

I am currently working on developing higher-order React components that will include default values for components labeled as "named". Here is a basic implementation example: type SomeProps = { a: string } type Variants = 'variantA' | 'var ...

Exploring the world of TypeScript interfaces and their uses with dynamic keys

I am hopeful that this can be achieved. The requirement is quite simple - I have 2 different types. type Numbers: Number[]; type Name: string; Let's assume they are representing data retrieved from somewhere: // the first provider sends data in th ...

Exploring methods to successfully upload a blob to Firebase and modify it using cloud functions

For days now, I've been attempting to successfully upload a file to firestorage using firebase functions but haven't had any luck. This is the progress I've made so far: export const tester = functions.https.onRequest(async (request, respons ...

Creating a custom Map type in TypeScript

I am exploring the concept of defining a Map type in Typescript using generics. Essentially, I want to create something similar to: EntityMap<U, V>, where U can only be either a string or a number This is what I have managed to come up with so far: ...

I encountered a problem while integrating antd and moment.js in my React project

I am currently using the antd date-picker in my React project with TypeScript. Encountered an error: Uncaught Type Error: moment is not a function. If anyone has a solution, please assist me. .tsx file:: const dateFormat = 'MM-DD-YYYY'; < ...

What could be causing the error when my file is running?

Whenever I attempt to run a file using the command node database.ts, an error pops up. Can someone help me identify what's wrong with my syntax? This is how the file appears: import { Sequelize } from 'sequelize-typescript'; export const ...

Error encountered in app.module.ts file of Angular 2 application

My friends and I are working on a big school project, creating a cool web app. Suddenly, I encountered some errors in my app.module.ts file that I haven't seen before. It's strange because they are showing up out of nowhere! The error: Error:( ...

Can you explain to me the significance of `string[7]` in TypeScript?

As I was working in TypeScript today, I encountered a situation where I needed to type a field to a string array with a specific size. Despite knowing how to accomplish this in TS, my instincts from writing code in C led me to initially write the following ...

Directive for creating a custom loading indicator in Angular

I have created a custom Angular element directive that displays and hides a loading indicator based on a condition from a service call. The directive is used as an element within another element. While the directive itself works correctly, the issue is tha ...

Investigating SCSS Issues: The Problem with Absolute Imports

I am working on a project with the following structure: - my/project - assets - images - image-name.svg - source - components - MyComponent - MyComponent.module.scss - ... - ... ...

TypeScript combined with Vue 3: Uncaught ReferenceError - variable has not been declared

At the start of my <script>, I define a variable with type any. Later on, within the same script, I reference this variable in one of my methods. Strangely, although my IDE does not raise any complaints, a runtime error occurs in my console: Referenc ...

The value stored within an object does not automatically refresh when using the useState hook

const increaseOffsetBy24 = () => { setHasMore(false); dispatch(contentList(paramsData)); setParamsData((prevState) => ({ ...prevState, offset: prevState.offset + 24, })); setHasMore(true); }; This function increment ...