
Research
/Security News
Shai Hulud Strikes Again (v2)
Another wave of Shai-Hulud campaign has hit npm with more than 500 packages and 700+ versions affected.
@qiwi/mixin
Advanced tools
RnD project to compare various mixin approaches in TypeScript.
^12.20.0 || ^14.13.1 || >=16.0.0>= 3.7 | 4.xyarn add @qiwi/mixin
npm i @qiwi/mixin
import {applyMixins} from '@qiwi/mixin'
interface IA {
a: () => string
}
interface IB {
b: () => string
}
class A implements IA {
a() { return 'a' }
}
const b: IB = {
b() { return 'b' }
}
const c = applyMixins({}, A, b)
c.a() // 'a'
c.b() // 'b'
const D = applyMixins(A, b)
const d = new D()
d.a() // 'a'
d.b() // 'b'
The library exposes itself as cjs, esm, umd and ts sources.
Follow packages.json test:it:* scripts and integration tests examples if you're having troubles with loading.
import {
applyMixinsAsProxy,
applyMixinsAsMerge,
applyMixinsAsSubclass,
applyMixinsAsProto,
applyMixinsAsPipe
} from '@qiwi/mixin'
interface A {
a(): string
}
interface B extends A {
b(): string
}
interface C extends B {
c(): string
}
interface D {
d(): number
}
const a: A = {
a() {
return 'a'
},
}
const _a: A = {
a() {
return '_a'
},
}
const b = {
b() {
return this.a().toUpperCase()
},
} as B
const c = {
c() {
return this.a() + this.b()
},
} as C
class ACtor implements A {
a() {
return 'a'
}
static foo() {
return 'foo'
}
}
class BCtor extends ACtor implements B {
b() {
return this.a().toUpperCase()
}
static bar() {
return 'bar'
}
}
class DCtor implements D {
d() {
return 1
}
}
class Blank {}
type ITarget = { foo: string }
const t: ITarget = {foo: 'bar'}
const t2 = applyMixinsAsProxy(t, a, b, c, _a)
t2.c() // '_a_A'
t2.a() // '_a'
t2.foo // 'bar'
// @ts-ignore
t2.d // undefined
type ITarget = { foo: string }
const t: ITarget = {foo: 'bar'}
const t2 = applyMixinsAsMerge(t, a, b, c)
t === t2 // true
t2.c() // 'aA'
t2.a() // 'a'
t2.foo // 'bar'
const M = applyMixinsAsSubclass(ACtor, Blank, BCtor, DCtor)
const m = new M()
M.foo() // 'foo'
M.bar() // 'bar'
m instanceof M // true
m instanceof ACtor // true
m.a() // 'a'
m.b() // 'A'
m.d() // 1
class Target {
method() {
return 'value'
}
}
const Derived = applyMixinsAsProto(Target, ACtor, BCtor, DCtor, Blank)
const m = new Derived()
Derived === Target // true
Derived.foo() // 'foo'
Derived.bar() // 'bar'
m.a() // 'a'
m.b() // 'A'
m.d() // 1
const n = (n: number) => ({n})
const m = ({n}: {n: number}) => ({n: 2 * n})
const k = ({n}: {n: string}) => n.toUpperCase()
const e = <T extends {}>(e: T): T & {foo: string} => ({...e, foo: 'foo'})
const i = <T extends {foo: number}>(i: T): T => i
const nm = applyMixinsAsPipe(n, m)
const ie = applyMixinsAsPipe(i, e)
const v1: number = nm(2).n // 4
const v2: string = ie({foo: 1}).foo // 'foo'
A mixin is a special kind of multiple inheritance.
There're several solutions:
- A subclass factory
- Proto merge + constructor invocation + type cast workarounds
Apply different merge strategies for each target type and rest args converters
Ref Cache / WeakMap
It's called
applyMixins
A mixin is a special kind of multiple inheritance. It's a form of object composition, where component features get mixed into a composite object so that properties of each mixin become properties of the composite object.
In OOP, a mixin is a class that contains methods for use by other classes, and can also be viewed as an interface with implemented methods.
Functional mixins are composable factories which connect together in a pipeline; each function adding some properties or behaviors.
Perhaps these are not perfect definitions, but we'll rely on them.
Subclass factory
type Constructor<T = {}> = new (...args: any[]) => T
function MixFoo<TBase extends Constructor>(Base: TBase) {
return class extends Base {
foo() { return 'bar' }
}
}
Prototype injection
class Derived {}
class Mixed {
foo() { return 'bar' }
}
Object.getOwnPropertyNames(Mixed.prototype).forEach(name => {
Object.defineProperty(Derived.prototype, name, Object.getOwnPropertyDescriptor(Mixed.prototype, name));
})
Object assignment
const foo = {foo: 'foo'}
const fooMixin = (target) => Object.assign(target, foo)
const bar = fooMixin({bar: 'bar'})
Proxy wrapping
const mixAsProxy = <P extends IAnyMap, M extends IAnyMap>(target: P, mixin: M): P & M => new Proxy(target, {
get: (obj, prop: string) => {
return prop in mixin
// @ts-ignore
? mixin[prop]
// @ts-ignore
: obj[prop]
},
}) as P & M
Functional mixin piping
const foo = <T>(t: T): T & {foo: string} => ({...t, foo: 'foo'})
const bar = <T>(t: T): T & {bar: number} => ({...t, bar: 1})
const foobar = pipe(foo, bar) // smth, that composes fn mixins like `(target) => bar(foo(target))`
const target = {}
const res = foobar(target)
FAQs
RnD project to compare various mixin approaches in TypeScript
The npm package @qiwi/mixin receives a total of 62 weekly downloads. As such, @qiwi/mixin popularity was classified as not popular.
We found that @qiwi/mixin demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 3 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
/Security News
Another wave of Shai-Hulud campaign has hit npm with more than 500 packages and 700+ versions affected.

Product
Add real-time Socket webhook events to your workflows to automatically receive software supply chain alert changes in real time.

Security News
ENISA has become a CVE Program Root, giving the EU a central authority for coordinating vulnerability reporting, disclosure, and cross-border response.