Dedupe JavaScript Class Mixins
npx @tessl/cli install tessl/npm-open-wc--dedupe-mixin@2.0.0Dedupe Mixin provides a utility function for automatically deduplicating JavaScript class mixins to prevent duplicate application of the same mixin in an inheritance chain. It uses a WeakMap to track which mixins have been applied to which classes, ensuring that each mixin is only applied once even when multiple inheritance paths attempt to apply the same mixin.
npm install @open-wc/dedupe-mixinimport { dedupeMixin } from '@open-wc/dedupe-mixin';For CommonJS (if bundled/transpiled):
const { dedupeMixin } = require('@open-wc/dedupe-mixin');import { dedupeMixin } from '@open-wc/dedupe-mixin';
// Create a mixin function
const LoggingMixin = (superClass) => class extends superClass {
log(message) {
console.log(`[${this.constructor.name}] ${message}`);
}
};
// Wrap with dedupeMixin to prevent duplicate application
const DedupedLoggingMixin = dedupeMixin(LoggingMixin);
// Create a base class
class BaseElement extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
}
// Apply the mixin once
class ComponentA extends DedupedLoggingMixin(BaseElement) {
connectedCallback() {
this.log('ComponentA connected');
}
}
// Apply the mixin again - it will be deduplicated
class ComponentB extends DedupedLoggingMixin(ComponentA) {
connectedCallback() {
super.connectedCallback();
this.log('ComponentB connected');
}
}
// Only one LoggingMixin is applied in the inheritance chain
const component = new ComponentB();Wraps a mixin function to ensure it is only applied once in any inheritance chain.
/**
* Apply each mixin in the chain to make sure they are not applied more than once to the final class.
* @param {function} mixin - Mixin to be applied
* @returns {function} Mixed class factory with mixin applied only once
*/
function dedupeMixin(mixin);The dedupeMixin function:
Usage Examples:
import { dedupeMixin } from '@open-wc/dedupe-mixin';
// Define a mixin
const TimestampMixin = (superClass) => class extends superClass {
getTimestamp() {
return new Date().toISOString();
}
};
// Create deduped version
const DedupedTimestampMixin = dedupeMixin(TimestampMixin);
// Multiple applications - only applies once
class Base {}
class A extends DedupedTimestampMixin(Base) {}
class B extends DedupedTimestampMixin(A) {} // Mixin not applied again
const instance = new B();
console.log(instance.getTimestamp()); // Works correctly// Complex inheritance with multiple mixins
const MixinA = dedupeMixin((superClass) => class extends superClass {
featureA() { return 'A'; }
});
const MixinB = dedupeMixin((superClass) => class extends superClass {
featureB() { return 'B'; }
});
const MixinC = dedupeMixin((superClass) => class extends superClass {
featureC() { return 'C'; }
});
// Compose mixins - each only applied once despite multiple paths
const ComposedMixin = (superClass) => MixinA(MixinB(MixinC(superClass)));
const AnotherMixin = (superClass) => MixinC(MixinA(MixinB(superClass)));
class Final extends AnotherMixin(ComposedMixin(Base)) {}
// Each mixin (A, B, C) is only applied once despite multiple inheritance paths/**
* Generic constructor type for class constructors
*/
type Constructor<T> = new (...args: any[]) => T;
/**
* Dedupe mixin function with TypeScript support
* @param mixin - A mixin function that extends a constructor
* @returns The same mixin function with deduplication behavior
*/
declare function dedupeMixin<T extends (s: Constructor<any>) => any>(mixin: T): T;The TypeScript definitions provide: