DEV Community

Cover image for Mixins in typescript
Pratik sharma
Pratik sharma

Posted on • Originally published at blog.coolhead.in

5

Mixins in typescript

A mixin is an abstract subclass; i.e. a subclass definition that may be applied to different superclasses to create a related family of modified classes.

Gilad Bracha and William Cook, Mixin-based Inheritance

  • mixin definition: The definition of a class that may be applied to different superclasses.

  • mixin application: The application of a mixin definition to a specific superclass, producing a new subclass.

Mixin libraries like Cocktail, traits.js, and patterns described in many blog posts (like one of the latest to hit Hacker News: Using ES7 Decorators as Mixins), generally work by modifying objects in place, copying in properties from mixin objects and overwriting existing properties.

This is often implemented via a function similar to this:

function mixin(source, target) {
  for (var prop in source) {
    if (source.hasOwnProperty(prop)) {
      target[prop] = source[prop];
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

A version of this has even made it into JavaScript as Object.assign.

mixin() is usually then called on a prototype:

mixin(MyMixin, MyClass.prototype);
Enter fullscreen mode Exit fullscreen mode

and now MyClass has all the properties defined in MyMixin.

functors: function which creates a function

function myloggerFunction() {
  return (str) => {console.log(str)}
}

const logger1 = myloggerFunction();

logger1('hello');
Enter fullscreen mode Exit fullscreen mode

functions that creates class

function myloggerFunction() {
    return class MyLoggerClass() {
        private 
    }
}
Enter fullscreen mode Exit fullscreen mode
function myLogFunction() {
  return (str: string) => {
    console.log(str);
  };
}

function myLoggerClass() {
  return new (class Logger {
    private completeLog: string = "";
    log(str: string) {
      console.log(str);
      this.completeLog += `${str}\n`;
    }
    dumpLog() {
      return this.completeLog;
    }
  })();
}

function SimpleMemoryDatabase<T>() {
  return class SimpleMemoryDatabase {
    private db: Record<string, T> = {};

    set(id: string, value: T): void {
      this.db[id] = value;
    }

    get(id: string): T {
      return this.db[id];
    }

    getObject(): Record<string, T> {
      return this.db;
    }
  };
}

const StringDatabase = SimpleMemoryDatabase<string>();

const sdb1 = new StringDatabase();
sdb1.set("name", "Jack");
console.log(sdb1.get("name"));

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

function Dumpable<
  T extends Constructor<{
    getObject(): object;
  }>
>(Base: T) {
  return class Dumpable extends Base {
    dump() {
      console.log(this.getObject());
    }
  };
}

const DumpableStringDatabase = Dumpable(StringDatabase);
const sdb2 = new DumpableStringDatabase();
sdb2.set("name", "Jack");
sdb2.dump();
Enter fullscreen mode Exit fullscreen mode

[A mixin is] a function that

  1. takes a constructor,

  2. creates a class that extends that constructor with new functionality

  3. returns the new class

// Needed for all mixins
type Constructor<T = {}> = new (...args: any[]) => T;

////////////////////
// Example mixins
////////////////////

// A mixin that adds a property
function Timestamped<TBase extends Constructor>(Base: TBase) {
  return class extends Base {
    timestamp = Date.now();
  };
}

// a mixin that adds a property and methods
function Activatable<TBase extends Constructor>(Base: TBase) {
  return class extends Base {
    isActivated = false;

    activate() {
      this.isActivated = true;
    }

    deactivate() {
      this.isActivated = false;
    }
  };
}

////////////////////
// Usage to compose classes
////////////////////

// Simple class
class User {
  name = '';
}

// User that is Timestamped
const TimestampedUser = Timestamped(User);

// User that is Timestamped and Activatable
const TimestampedActivatableUser = Timestamped(Activatable(User));

////////////////////
// Using the composed classes
////////////////////

const timestampedUserExample = new TimestampedUser();
console.log(timestampedUserExample.timestamp);

const timestampedActivatableUserExample = new TimestampedActivatableUser();
console.log(timestampedActivatableUserExample.timestamp);
console.log(timestampedActivatableUserExample.isActivated);
Enter fullscreen mode Exit fullscreen mode
// Define a simple class with a greet method
class Greeter {
  greet(name: string) {
    console.log(`Hello, ${name}!`);
  }
}

// Define a mixin that adds a log method to a class
type Loggable = { log(message: string): void };

function withLogging<T extends new (...args: any[]) => Loggable>(Base: T) {
  return class extends Base {
    log(message: string) {
      console.log(`[${new Date().toISOString()}] ${message}`);
    }
  };
}

// Create a new class that combines the Greeter and Loggable mixins
const MyGreeter = withLogging(Greeter);

// Use the new class to create an instance and call its methods
const greeter = new MyGreeter();
greeter.greet("Alice"); // Output: "Hello, Alice!"
greeter.log("An event occurred."); // Output: "[2023-04-04T12:00:00.000Z] An event occurred."
Enter fullscreen mode Exit fullscreen mode

This is just the beginning of many topics related to mixins in JavaScript. I'll post more about things like:

  • Enhancing Mixins with Decorator Functions new post that covers:

  • Caching mixin applications so that the same mixin applied to the same superclass reuses a prototype.

  • Getting instanceof to work.

  • How mixin inheritance can address the fear that ES6 classes and classical inheritance are bad for JavaScript.

  • Using subclass-factory-style mixins in ES5.

  • De-duplicating mixins so mixin composition is more usable.

References:

https://bryntum.com/blog/the-mixin-pattern-in-typescript-all-you-need-to-know/

%[https://www.youtube.com/watch?v=LvjNGo5ALyQ]

https://medium.com/@saif.adnan/typescript-mixin-ee962be3224d

https://egghead.io/lessons/typescript-use-the-optional-chaining-operator-in-typescript

https://javascript.info/mixins

Billboard image

Deploy and scale your apps on AWS and GCP with a world class developer experience

Coherence makes it easy to set up and maintain cloud infrastructure. Harness the extensibility, compliance and cost efficiency of the cloud.

Learn more

Top comments (0)

Cloudinary image

Optimize, customize, deliver, manage and analyze your images.

Remove background in all your web images at the same time, use outpainting to expand images with matching content, remove objects via open-set object detection and fill, recolor, crop, resize... Discover these and hundreds more ways to manage your web images and videos on a scale.

Learn more

👋 Kindness is contagious

Dive into an ocean of knowledge with this thought-provoking post, revered deeply within the supportive DEV Community. Developers of all levels are welcome to join and enhance our collective intelligence.

Saying a simple "thank you" can brighten someone's day. Share your gratitude in the comments below!

On DEV, sharing ideas eases our path and fortifies our community connections. Found this helpful? Sending a quick thanks to the author can be profoundly valued.

Okay