A TypeScript library that provides tolerable Mixin functionality with multiple inheritance support.
—
Global configuration settings for controlling mixin behavior. The settings object allows you to customize how ts-mixer handles prototype mixing, static properties, constructor initialization, and decorator inheritance.
Global configuration object that controls ts-mixer behavior across all mixin operations.
/**
* Global settings object for configuring ts-mixer behavior
* Changes affect all subsequent mixin operations
*/
interface Settings {
/** Name of init function to call after construction, null to disable */
initFunction: string | null;
/** Strategy for handling static properties: 'copy' or 'proxy' */
staticsStrategy: "copy" | "proxy";
/** Strategy for mixing prototypes: 'copy' or 'proxy' */
prototypeStrategy: "copy" | "proxy";
/** Decorator inheritance strategy: 'deep', 'direct', or 'none' */
decoratorInheritance: "deep" | "direct" | "none";
}
/**
* Global settings instance - modify properties to change behavior
*/
declare const settings: Settings;Controls how class prototypes are mixed together.
import { settings } from "ts-mixer";
// Copy strategy (default) - better performance, ES5 compatible
settings.prototypeStrategy = "copy";
// Proxy strategy - dynamic updates, ES6+ only
settings.prototypeStrategy = "proxy";Copy Strategy ("copy"):
Proxy Strategy ("proxy"):
import { Mixin, settings } from "ts-mixer";
class Base {
getValue(): string {
return "original";
}
}
class Other {
getOther(): string {
return "other";
}
}
// Test copy strategy
settings.prototypeStrategy = "copy";
class CopyMixed extends Mixin(Base, Other) {}
const copyInstance = new CopyMixed();
console.log(copyInstance.getValue()); // "original"
// Modify base class after mixing
Base.prototype.getValue = () => "modified";
console.log(copyInstance.getValue()); // Still "original" - not updated
// Test proxy strategy
settings.prototypeStrategy = "proxy";
class ProxyMixed extends Mixin(Base, Other) {}
const proxyInstance = new ProxyMixed();
console.log(proxyInstance.getValue()); // "modified" - dynamically updatedControls how static properties and methods are inherited.
import { settings } from "ts-mixer";
// Copy strategy (default)
settings.staticsStrategy = "copy";
// Proxy strategy
settings.staticsStrategy = "proxy";Copy Strategy ("copy"):
Proxy Strategy ("proxy"):
import { Mixin, settings } from "ts-mixer";
class BaseClass {
static baseStaticProp: string = "base";
static baseStaticMethod(): string {
return "base method";
}
}
class OtherClass {
static otherStaticProp: string = "other";
}
// Test copy strategy
settings.staticsStrategy = "copy";
class CopyMixed extends Mixin(BaseClass, OtherClass) {}
console.log(CopyMixed.baseStaticProp); // "base"
console.log(CopyMixed.baseStaticMethod()); // "base method"
// Modify base class static after mixing
BaseClass.baseStaticProp = "modified base";
console.log(CopyMixed.baseStaticProp); // Still "base" - not updated
// Test proxy strategy
settings.staticsStrategy = "proxy";
class ProxyMixed extends Mixin(BaseClass, OtherClass) {}
console.log(ProxyMixed.baseStaticProp); // "modified base" - dynamically updatedSpecifies a function name to call after construction with proper this context.
import { settings } from "ts-mixer";
// Enable init function (disabled by default)
settings.initFunction = "init";
// Disable init function
settings.initFunction = null;Purpose:
ES6 constructor limitations prevent ts-mixer from passing the correct this to constructors. The init function workaround allows constructor-like behavior with proper this context.
Usage Pattern:
import { Mixin, settings } from "ts-mixer";
settings.initFunction = "init";
class Person {
public static allPeople: Set<Person> = new Set();
name: string;
constructor(name: string) {
this.name = name;
// Cannot use 'this' for side effects here in mixins
}
// Init function receives proper 'this' context
protected init(name: string) {
Person.allPeople.add(this); // This works correctly
}
}
class Employee {
public static allEmployees: Set<Employee> = new Set();
employeeId: number;
constructor(name: string, employeeId: number) {
this.employeeId = employeeId;
}
protected init(name: string, employeeId: number) {
Employee.allEmployees.add(this); // This works correctly
}
}
class PersonEmployee extends Mixin(Person, Employee) {}
// Both constructors and both init functions are called
const emp = new PersonEmployee("John Doe", 12345);
console.log(Person.allPeople.has(emp)); // true - init function worked
console.log(Employee.allEmployees.has(emp)); // true - init function worked
console.log(emp.name); // "John Doe" - constructor worked
console.log(emp.employeeId); // 12345 - constructor workedInit Function Features:
this context (unlike constructors in mixins)Controls how decorators are inherited during mixing.
import { settings } from "ts-mixer";
// Deep inheritance (default) - inherit from all ancestors
settings.decoratorInheritance = "deep";
// Direct inheritance - only from direct mixin classes
settings.decoratorInheritance = "direct";
// No inheritance - disable decorator inheritance
settings.decoratorInheritance = "none";Deep Inheritance ("deep"):
Direct Inheritance ("direct"):
Mixin()None ("none"):
import { Mixin, decorate, settings } from "ts-mixer";
function MyDecorator(name: string) {
return function(target: any) {
target.decoratorApplied = name;
};
}
@decorate(MyDecorator("grandparent"))
class GrandParent {}
@decorate(MyDecorator("parent"))
class Parent extends GrandParent {}
@decorate(MyDecorator("mixin"))
class MixinClass {}
// Deep inheritance
settings.decoratorInheritance = "deep";
class DeepMixed extends Mixin(Parent, MixinClass) {}
// Inherits decorators from: GrandParent, Parent, MixinClass
// Direct inheritance
settings.decoratorInheritance = "direct";
class DirectMixed extends Mixin(Parent, MixinClass) {}
// Inherits decorators from: Parent, MixinClass (not GrandParent)
// No inheritance
settings.decoratorInheritance = "none";
class NoDecorators extends Mixin(Parent, MixinClass) {}
// Inherits no decoratorsimport { settings } from "ts-mixer";
// Optimize for performance
settings.prototypeStrategy = "copy"; // Faster property access
settings.staticsStrategy = "copy"; // Faster static access
settings.decoratorInheritance = "none"; // Skip decorator processing
settings.initFunction = null; // Skip init function calls
// Use for high-performance scenarios where you don't need:
// - Dynamic prototype updates
// - Dynamic static updates
// - Decorator inheritance
// - Init function workaroundsimport { settings } from "ts-mixer";
// Optimize for dynamic behavior
settings.prototypeStrategy = "proxy"; // Dynamic prototype updates
settings.staticsStrategy = "proxy"; // Dynamic static updates
settings.decoratorInheritance = "deep"; // Full decorator inheritance
settings.initFunction = "init"; // Enable init functions
// Use when you need:
// - Runtime modifications to base classes
// - Full decorator support
// - Constructor workaroundsimport { settings } from "ts-mixer";
// Balanced approach (default settings)
settings.prototypeStrategy = "copy"; // Good performance
settings.staticsStrategy = "copy"; // Good performance
settings.decoratorInheritance = "deep"; // Full decorator support
settings.initFunction = null; // Disabled by default
// Good for most use cases providing:
// - Reasonable performance
// - Full decorator inheritance
// - Simple constructor behaviorSettings can be changed at runtime and affect all subsequent mixin operations:
import { Mixin, settings } from "ts-mixer";
// Initial setting
settings.prototypeStrategy = "copy";
class A {}
class B {}
class Mixed1 extends Mixin(A, B) {} // Uses copy strategy
// Change setting
settings.prototypeStrategy = "proxy";
class Mixed2 extends Mixin(A, B) {} // Uses proxy strategy
// Mixed1 still uses copy, Mixed2 uses proxyImportant Notes:
Install with Tessl CLI
npx tessl i tessl/npm-ts-mixer